diff options
author | Thomas Groman <tgroman@nuegia.net> | 2020-04-20 20:49:37 -0700 |
---|---|---|
committer | Thomas Groman <tgroman@nuegia.net> | 2020-04-20 20:49:37 -0700 |
commit | f9cab004186edb425a9b88ad649726605080a17c (patch) | |
tree | e2dae51d3144e83d097a12e7a1499e3ea93f90be /components | |
parent | f428692de8b59ab89a66502c079e1823dfda8aeb (diff) | |
download | webbrowser-f9cab004186edb425a9b88ad649726605080a17c.tar webbrowser-f9cab004186edb425a9b88ad649726605080a17c.tar.gz webbrowser-f9cab004186edb425a9b88ad649726605080a17c.tar.lz webbrowser-f9cab004186edb425a9b88ad649726605080a17c.tar.xz webbrowser-f9cab004186edb425a9b88ad649726605080a17c.zip |
move browser to webbrowser/
Diffstat (limited to 'components')
263 files changed, 0 insertions, 66150 deletions
diff --git a/components/BrowserComponents.manifest b/components/BrowserComponents.manifest deleted file mode 100644 index 0ff14d0..0000000 --- a/components/BrowserComponents.manifest +++ /dev/null @@ -1,64 +0,0 @@ -# nsAboutRedirector.js -component {8cc51368-6aa0-43e8-b762-bde9b9fd828c} nsAboutRedirector.js -# Each entry here should be coupled with an entry in nsAboutRedirector.js -contract @mozilla.org/network/protocol/about;1?what=certerror {8cc51368-6aa0-43e8-b762-bde9b9fd828c} -contract @mozilla.org/network/protocol/about;1?what=downloads {8cc51368-6aa0-43e8-b762-bde9b9fd828c} -contract @mozilla.org/network/protocol/about;1?what=feeds {8cc51368-6aa0-43e8-b762-bde9b9fd828c} -contract @mozilla.org/network/protocol/about;1?what=home {8cc51368-6aa0-43e8-b762-bde9b9fd828c} -contract @mozilla.org/network/protocol/about;1?what=newtab {8cc51368-6aa0-43e8-b762-bde9b9fd828c} -contract @mozilla.org/network/protocol/about;1?what=palemoon {8cc51368-6aa0-43e8-b762-bde9b9fd828c} -contract @mozilla.org/network/protocol/about;1?what=permissions {8cc51368-6aa0-43e8-b762-bde9b9fd828c} -contract @mozilla.org/network/protocol/about;1?what=privatebrowsing {8cc51368-6aa0-43e8-b762-bde9b9fd828c} -contract @mozilla.org/network/protocol/about;1?what=rights {8cc51368-6aa0-43e8-b762-bde9b9fd828c} -contract @mozilla.org/network/protocol/about;1?what=sessionrestore {8cc51368-6aa0-43e8-b762-bde9b9fd828c} -#ifdef MOZ_SERVICES_SYNC -contract @mozilla.org/network/protocol/about;1?what=sync-progress {8cc51368-6aa0-43e8-b762-bde9b9fd828c} -contract @mozilla.org/network/protocol/about;1?what=sync-tabs {8cc51368-6aa0-43e8-b762-bde9b9fd828c} -#endif - -# nsBrowserContentHandler.js -component {5d0ce354-df01-421a-83fb-7ead0990c24e} nsBrowserContentHandler.js application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -contract @mozilla.org/browser/clh;1 {5d0ce354-df01-421a-83fb-7ead0990c24e} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -component {47cd0651-b1be-4a0f-b5c4-10e5a573ef71} nsBrowserContentHandler.js application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -contract @mozilla.org/browser/final-clh;1 {47cd0651-b1be-4a0f-b5c4-10e5a573ef71} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -contract @mozilla.org/uriloader/content-handler;1?type=text/html {5d0ce354-df01-421a-83fb-7ead0990c24e} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -contract @mozilla.org/uriloader/content-handler;1?type=application/vnd.mozilla.xul+xml {5d0ce354-df01-421a-83fb-7ead0990c24e} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -contract @mozilla.org/uriloader/content-handler;1?type=image/svg+xml {5d0ce354-df01-421a-83fb-7ead0990c24e} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -contract @mozilla.org/uriloader/content-handler;1?type=text/rdf {5d0ce354-df01-421a-83fb-7ead0990c24e} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -contract @mozilla.org/uriloader/content-handler;1?type=text/xml {5d0ce354-df01-421a-83fb-7ead0990c24e} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -contract @mozilla.org/uriloader/content-handler;1?type=application/xhtml+xml {5d0ce354-df01-421a-83fb-7ead0990c24e} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -contract @mozilla.org/uriloader/content-handler;1?type=text/css {5d0ce354-df01-421a-83fb-7ead0990c24e} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -contract @mozilla.org/uriloader/content-handler;1?type=text/plain {5d0ce354-df01-421a-83fb-7ead0990c24e} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -contract @mozilla.org/uriloader/content-handler;1?type=image/gif {5d0ce354-df01-421a-83fb-7ead0990c24e} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -contract @mozilla.org/uriloader/content-handler;1?type=image/jpeg {5d0ce354-df01-421a-83fb-7ead0990c24e} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -contract @mozilla.org/uriloader/content-handler;1?type=image/jpg {5d0ce354-df01-421a-83fb-7ead0990c24e} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -contract @mozilla.org/uriloader/content-handler;1?type=image/png {5d0ce354-df01-421a-83fb-7ead0990c24e} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -contract @mozilla.org/uriloader/content-handler;1?type=image/bmp {5d0ce354-df01-421a-83fb-7ead0990c24e} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -contract @mozilla.org/uriloader/content-handler;1?type=image/x-icon {5d0ce354-df01-421a-83fb-7ead0990c24e} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -contract @mozilla.org/uriloader/content-handler;1?type=image/vnd.microsoft.icon {5d0ce354-df01-421a-83fb-7ead0990c24e} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -contract @mozilla.org/uriloader/content-handler;1?type=image/webp {5d0ce354-df01-421a-83fb-7ead0990c24e} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -contract @mozilla.org/uriloader/content-handler;1?type=application/http-index-format {5d0ce354-df01-421a-83fb-7ead0990c24e} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -category command-line-handler m-browser @mozilla.org/browser/clh;1 application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -category command-line-handler x-default @mozilla.org/browser/final-clh;1 application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -category command-line-validator b-browser @mozilla.org/browser/clh;1 application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} - -# nsBrowserGlue.js - -# WebappRT doesn't need these instructions, and they don't necessarily work -# with it, but it does use a GRE directory that the GRE shares with Firefox, -# so in order to prevent the instructions from being processed for WebappRT, -# we need to restrict them to the applications that depend on them, i.e.: -# -# b2g: {3c2e2abc-06d4-11e1-ac3b-374f68613e61} -# browser: {8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -# mobile/android: {aa3c5121-dab2-40e2-81ca-7ea25febc110} -# mobile/xul: {a23983c0-fd0e-11dc-95ff-0800200c9a66} -# -# In theory we should do this for all these instructions, but in practice it is -# sufficient to do it for the app-startup one, and the file is simpler that way. - -component {eab9012e-5f74-4cbc-b2b5-a590235513cc} nsBrowserGlue.js -contract @mozilla.org/browser/browserglue;1 {eab9012e-5f74-4cbc-b2b5-a590235513cc} -category app-startup nsBrowserGlue service,@mozilla.org/browser/browserglue;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66} -component {d8903bf6-68d5-4e97-bcd1-e4d3012f721a} nsBrowserGlue.js -contract @mozilla.org/content-permission/prompt;1 {d8903bf6-68d5-4e97-bcd1-e4d3012f721a} diff --git a/components/abouthome/aboutHome.css b/components/abouthome/aboutHome.css deleted file mode 100644 index 2b062e8..0000000 --- a/components/abouthome/aboutHome.css +++ /dev/null @@ -1,343 +0,0 @@ -%if 0 -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -%endif - -html { - font: message-box; - font-size: 100%; - background-color: hsl(0,0%,90%); - color: #000; - height: 100%; -} - -body { - margin: 0; - display: -moz-box; - -moz-box-orient: vertical; - width: 100%; - height: 100%; - background-image: url(chrome://browser/content/abouthome/noise.png), - linear-gradient(hsla(0,0%,100%,.7), hsla(0,0%,100%,.4)); -} - -input, -button { - font-size: inherit; - font-family: inherit; -} - -a { - color: -moz-nativehyperlinktext; - text-decoration: none; -} - -.spacer { - -moz-box-flex: 1; -} - -#topSection { - text-align: center; -} - -#brandLogo { - height: 192px; - width: 192px; - margin: 22px auto 31px; - background-image: url("chrome://branding/content/about-logo.png"); - background-size: 192px auto; - background-position: center center; - background-repeat: no-repeat; -} - -#searchForm { - width: 470px; -} - -#searchForm { - display: -moz-box; -} - -#searchLogoContainer { - display: -moz-box; - -moz-box-align: center; - padding-top: 2px; - -moz-padding-end: 8px; -} - -#searchLogoContainer[hidden] { - display: none; -} - -#searchEngineLogo { - display: inline-block; - height: 28px; - width: 70px; - min-width: 70px; -} - -#searchText { - -moz-box-flex: 1; - padding: 6px 8px; - background: hsla(0,0%,100%,.9) padding-box; - border: 1px solid; - border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2); - box-shadow: 0 1px 0 hsla(210,65%,9%,.02) inset, - 0 0 2px hsla(210,65%,9%,.1) inset, - 0 1px 0 hsla(0,0%,100%,.2); - border-radius: 2.5px 0 0 2.5px; -} - -#searchText:-moz-dir(rtl) { - border-radius: 0 2.5px 2.5px 0; -} - -#searchText:focus, -#searchText[autofocus] { - border-color: hsla(206,100%,60%,.6) hsla(206,76%,52%,.6) hsla(204,100%,40%,.6); -} - -#searchSubmit { - -moz-margin-start: -1px; - background: linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.1)) padding-box; - padding: 0 9px; - border: 1px solid; - border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2); - -moz-border-start: 1px solid transparent; - border-radius: 0 2.5px 2.5px 0; - box-shadow: 0 0 2px hsla(0,0%,100%,.5) inset, - 0 1px 0 hsla(0,0%,100%,.2); - cursor: pointer; - transition-property: background-color, border-color, box-shadow; - transition-duration: 150ms; -} - -#searchSubmit:-moz-dir(rtl) { - border-radius: 2.5px 0 0 2.5px; -} - -#searchText:focus + #searchSubmit, -#searchText + #searchSubmit:hover, -#searchText[autofocus] + #searchSubmit { - border-color: #59b5fc #45a3e7 #3294d5; - color: white; -} - -#searchText:focus + #searchSubmit, -#searchText[autofocus] + #searchSubmit { - background-image: linear-gradient(#4cb1ff, #1793e5); - box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset, - 0 0 0 1px hsla(0,0%,100%,.1) inset, - 0 1px 0 hsla(210,54%,20%,.03); -} - -#searchText + #searchSubmit:hover { - background-image: linear-gradient(#66bdff, #0d9eff); - box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset, - 0 0 0 1px hsla(0,0%,100%,.1) inset, - 0 1px 0 hsla(210,54%,20%,.03), - 0 0 4px hsla(206,100%,20%,.2); -} - -#searchText + #searchSubmit:hover:active { - box-shadow: 0 1px 1px hsla(211,79%,6%,.1) inset, - 0 0 1px hsla(211,79%,6%,.2) inset; - transition-duration: 0ms; -} - -#launcher { - display: -moz-box; - -moz-box-align: center; - -moz-box-pack: center; - width: 100%; - background-color: hsla(0,0%,0%,.03); - border-top: 1px solid hsla(0,0%,0%,.03); - box-shadow: 0 1px 2px hsla(0,0%,0%,.02) inset, - 0 -1px 0 hsla(0,0%,100%,.25); -} - -#launcher:not([session]), -body[narrow] #launcher[session] { - display: block; /* display separator and restore button on separate lines */ - text-align: center; - white-space: nowrap; /* prevent navigational buttons from wrapping */ -} - -.launchButton { - display: -moz-box; - -moz-box-orient: vertical; - margin: 16px 1px; - padding: 14px 6px; - min-width: 88px; - max-width: 176px; - max-height: 85px; - vertical-align: top; - white-space: normal; - background: transparent padding-box; - border: 1px solid transparent; - border-radius: 2.5px; - color: #525c66; - font-size: 75%; - cursor: pointer; - transition-property: background-color, border-color, box-shadow; - transition-duration: 150ms; -} - -body[narrow] #launcher[session] > .launchButton { - margin: 4px 1px; -} - -.launchButton:hover { - background-color: hsla(211,79%,6%,.03); - border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2); -} - -.launchButton:hover:active { - background-image: linear-gradient(hsla(211,79%,6%,.02), hsla(211,79%,6%,.05)); - border-color: hsla(210,54%,20%,.2) hsla(210,54%,20%,.23) hsla(210,54%,20%,.25); - box-shadow: 0 1px 1px hsla(211,79%,6%,.05) inset, - 0 0 1px hsla(211,79%,6%,.1) inset; - transition-duration: 0ms; -} - -.launchButton[hidden], -#launcher:not([session]) > #restorePreviousSessionSeparator, -#launcher:not([session]) > #restorePreviousSession { - display: none; -} - -#restorePreviousSessionSeparator { - width: 3px; - height: 116px; - margin: 0 10px; - background-image: linear-gradient(hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0)), - linear-gradient(hsla(211,79%,6%,0), hsla(211,79%,6%,.2), hsla(211,79%,6%,0)), - linear-gradient(hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0)); - background-position: left top, center, right bottom; - background-size: 1px auto; - background-repeat: no-repeat; -} - -body[narrow] #restorePreviousSessionSeparator { - margin: 0 auto; - width: 512px; - height: 3px; - background-image: linear-gradient(to right, hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0)), - linear-gradient(to right, hsla(211,79%,6%,0), hsla(211,79%,6%,.2), hsla(211,79%,6%,0)), - linear-gradient(to right, hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0)); - background-size: auto 1px; -} - -#restorePreviousSession { - max-width: none; - font-size: 90%; -} - -body[narrow] #restorePreviousSession { - font-size: 80%; -} - -.launchButton::before { - display: block; - width: 32px; - height: 32px; - margin: 0 auto 6px; - line-height: 0; /* remove extra vertical space due to non-zero font-size */ -} - -#downloads::before { - content: url("chrome://browser/content/abouthome/downloads.png"); -} - -#bookmarks::before { - content: url("chrome://browser/content/abouthome/bookmarks.png"); -} - -#history::before { - content: url("chrome://browser/content/abouthome/history.png"); -} - -#addons::before { - content: url("chrome://browser/content/abouthome/addons.png"); -} - -%ifdef MOZ_SERVICES_SYNC -#sync::before { - content: url("chrome://browser/content/abouthome/sync.png"); -} -%endif - -#settings::before { - content: url("chrome://browser/content/abouthome/settings.png"); -} - -#restorePreviousSession::before { - content: url("chrome://browser/content/abouthome/restore-large.png"); - height: 48px; - width: 48px; - display: inline-block; /* display on same line as text label */ - vertical-align: middle; - margin-bottom: 0; - -moz-margin-end: 8px; -} - -#restorePreviousSession:-moz-dir(rtl)::before { - transform: scaleX(-1); -} - -body[narrow] #restorePreviousSession::before { - content: url("chrome://browser/content/abouthome/restore.png"); - height: 32px; - width: 32px; -} - -/* [HiDPI] - * At resolutions above 1dppx, prefer downscaling the 2x Retina graphics - * rather than upscaling the original-size ones (bug 818940). - */ -@media not all and (max-resolution: 1dppx) { - #brandLogo { - background-image: url("chrome://branding/content/about-logo@2x.png"); - } - - .launchButton::before { - transform: scale(.5); - transform-origin: 0 0; - } - - #downloads::before { - content: url("chrome://browser/content/abouthome/downloads@2x.png"); - } - - #bookmarks::before { - content: url("chrome://browser/content/abouthome/bookmarks@2x.png"); - } - - #history::before { - content: url("chrome://browser/content/abouthome/history@2x.png"); - } - - #addons::before { - content: url("chrome://browser/content/abouthome/addons@2x.png"); - } - -%ifdef MOZ_SERVICES_SYNC - #sync::before { - content: url("chrome://browser/content/abouthome/sync@2x.png"); - } -%endif - - #settings::before { - content: url("chrome://browser/content/abouthome/settings@2x.png"); - } - - #restorePreviousSession::before { - content: url("chrome://browser/content/abouthome/restore-large@2x.png"); - } - - body[narrow] #restorePreviousSession::before { - content: url("chrome://browser/content/abouthome/restore@2x.png"); - } -} - diff --git a/components/abouthome/aboutHome.js b/components/abouthome/aboutHome.js deleted file mode 100644 index 6ff8eee..0000000 --- a/components/abouthome/aboutHome.js +++ /dev/null @@ -1,227 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -const SEARCH_ENGINES = { - "DuckDuckGo": { - image: "data:image/png;base64," + - "iVBORw0KGgoAAAANSUhEUgAAAIwAAAA4CAYAAAAvmxBdAAAVhUlEQVR4Xu3dd5SU1d3A8e/vPs/0" + - "2crussBSdkHEAgoomEQSUTAW3hRbfMUeUwgSj9FoorGXqDGxBHvMazRGE0KsBQuiEVRUEEEM0pfO" + - "1tndmZ32PPf3knDCUZAlIYsxOfM553f2v91/vnufOzP33BFV5TOnQFQ1snFN/YCVb88Z6S2dd1B8" + - "3Qf7lTSv6R9PNle4uXQEVNRxvUy4qL29pPeGRNXA5d6g4fOLhoyYN2C/oe8Vl5QmAoFAnm72GQqm" + - "oKO9vXj5e/NHtr48/fjq92eOq2xYOsixvuMpKFuhfJywjQMYI5oKF7evrR09t/LE7z3Ze9TYZyPx" + - "+FpjjPdfEkxBY0ND9ftP//7EkpceOLNm/cJh+J6rylYWcIwSiCHhuEo4ggRdMCLq+UomK5pJq2Y7" + - "BD8HqoIAAmKhPdKjuX7EMc9WnfCde/YZOfot13Xz/6HBFKi1pdmlCya23Dz5PPeDN/eygCqAqIn3" + - "ULduiAb2Ha3BfUYgJeUgBhxHRAwgoupbfF/wPcXL461bRX7xm5Jb8q7Yhno0lzUYMIANx9Lh0y99" + - "svjEc292YkXzAfufE0yBse0tX+qY+uNrOp/+9SGo5yggTlADQw72I4efQGDf4Wg6RW7xO5Jf8g7+" + - "ulVi21rRXAr8HKpWRBzFCSGRIpyKSnX6701wv0PU7Vunms2RmfO0ZGc/Z/zWjSKiAqJOdV1LyUVT" + - "7wkdcuQvENP8mQ+mQGPZt2ZelLj2nCl+Q30ZAqijoVFH+rGTJiHROJnXniE75znxN64yms8AKghd" + - "062DEZVIqQbq9tHwYcdpcL+DNDvvFUlNv1dsywYHA0jAjx512lslF956vkSL5n5Wgymwfq+O/7vx" + - "jvZfX/0/+FkXC27N3n7xlOvVlFdp8pFfSnbuC0bTbYKqIOw+BcSoKeut0WNPtZEjjtPOx++X1FMP" + - "GPysAXD777epxy1PXuj2qXsEsJ+hYArUy9e2Xn7GtPTLj44AFVVHY1/7tld0+g8l+cht2vnE/Y7N" + - "p0S2htJ9FEDUlPWxxZOusE5VjSRunIK3YbkrAhIpzlRMfeGy4P6jbwH8z0AwBZrPDWqacvQzmfkv" + - "D0ZETbxCS3/wC9/t1ZeWq78t3oZlDqiwp6nRyJiveMXnXEL7fdeTef1JV9UKKlp118wrQgeNvX5X" + - "0Rj2uMJjqOmik/6UmbclFkSdylrb4/qHfU0naTzvK463fqkLKijo1oGt0/3ESudrT7jNPznTxL8x" + - "iehXvuUhroJKw6RxV+aWzJ8MyL9vhSmIJm778fT2h244CiPqVg+0Pa64TzPzZtv2X18XUD8jAIiB" + - "3nWEK6rBDaHZTmyiCb+lGe1MoGpB6FZOWR+/7KJbbXb+n0lOv8tV64mJlnX2mr74ZKei11PshMue" + - "UmA6X3nyqrbf/uxIAKe4l5ZdcqdNz5vNllhc9TKCAIAaQ6puNLEzzqN86EhQRTs78BvWkX3/bTpf" + - "mkZm3p/RbAoM3cJrWe+03PB9yn881drOlJd85gHXT7VGG77/1TvK7n1pRThe/MGnuMIU+M2bj91w" + - "wrBHbUdDnEDUVlx2n29TbbT8/AIXLy18hAQiFJ8wmdD44wnvPwoxZvs9ENlFb9D2qxvIzH0BxNId" + - "VMGtGuBXXPNrm7j7OskueNkBKDnjkudKp1x7ItD5KQRToNavaLzgGy91vjr9ABAtPuUCL/LFo2m8" + - "8ETHJlsMwsek9zqEztMvRbw8TjBMqLSU4spKiquqicVjiAgANtVBx8O3kbjvOtTPgPCvUwjufZBX" + - "ftEt2njBScZv2+gYN5KvfvCN84N7H3DHpxBMQerNmZc3nHvU5ajnBGqHedW3Psam848jv+I9F2FH" + - "4qA4gIJvkHgZgeGHEvzSUZSMP4FQccnHVpvk0w+Seu73ZN57Hc11guFfo6JFX/+uFzpgNE1XnOUi" + - "KpEDvriy4p4XxrrB0Jo9GExB0+bNtanvjX/VX7mor6jR6rtmeOk3ZpJ46CZXRKWrx4MTK6fkrB8S" + - "n3AqTnkVuAFEgO0qU1Xw8ngbVpO462o6ZjyCGMu/RB3tOfUZr+03t5B5+/kAIhq7/g8/rTrqhEv3" + - "YDAFCx+889qiWyZfahVihx2fL598haw7ebRRmzbshCgEBgyj+rY/Eui/F/8UVVp+eTmt918HRvlX" + - "hOqGexWX3q4bvn2kg582nZW1awc9vuhL4Whs1R4IpqC1ubnXhm8d/mp45cK9cEK29/0v+22P3Elq" + - "xsMBhJ3Ssj7U/OYVwv0GsTvU99h03nGkXnsKEXabqqNVV96b75z9vCRf+kPAEWi5+P4fjvzfs2/e" + - "Ay+rC96f9fzYPqsX11mF2EGH+yYal9TMJ4wCKJ9ILAQmXbXbsWSyeVLpPGUX3ULm3Tfxk43sNrG0" + - "/eE+Uz7pMk29/Li1Nmeyj917QsexJ9xbVFzcDmDoFgWe5wWysx7/mvq+o1Y0NuEUOp6bpjaXEgV2" + - "Nuke/Sg6+n8B8H3LklWNzJq7gtXrW7BW6UpzopN7fj+X+6bNZdqCNuKnnof6oOzmqEr2w/cc9fMa" + - "2OsAtQoVq947YPVfFu/XzStMQWtTU1WPJXNHWwWnR28bHjZKWu+9AUVFlE+mkDxoPEXxCNYq055f" + - "yKamJGNHD0REUFVA2JlgwOGbJxxMLBKkrSNDONWTjkfvxG/dwO6yXobO2TMl+sVjNPPBO+pmM+FV" + - "s18cP3T0597oxmAKNqxYtm9R07oaayG0/0HqNW4mt26Vg4LyycSD7N6jcIFM3iMWDTH5lKEEXId/" + - "RFEsxN+VFkfQWDXxcceReHQqGHaPqnS+NctUXnyzlUBIfS8jzvzXxnieF3ZdN+PSLQo6PlhwcMxa" + - "Y30IH/h5Mu+/o9bLsCu58l4AhIMuR4/ZG9cx/LNS6RwbGzuorSkjfuTxtP7hLsBntwjkNq0T9TxM" + - "RV/1Ni2jdPUH+3q5XNFfgzF0hwLHXfmXA3wFcRwN7zuC9HvviKqC0uXkjYsCIrItlpa2TmbM/pCV" + - "a5tR1a5DTWWZ+MNHuPTWGbwwZxnBQfvi9hwAym6PptvFb20kWDsQtRBNbO6ZSyX7dNcjqUA1HG9a" + - "308VJF6qblVvydUvQa2KCjtlFGwqScazRAMOAIn2NOdc9kfqN7Ry8jEHcvyRQ6mrKWdn1m5KsHJd" + - "C9Fw4G97oKMO+SrBQUPIbVgBwu5RJbP8Qwn03UvVn4FR39H21kFUVi0wdIeCYDjRWKkKpqiHqlr1" + - "WpsEdvGfDLgNa2nPeADbVpctEeD7lufnLGXpqka6MnhAJRMnDKdf7zLO/NpIxA0QqKlF7XZ/a+uA" + - "bB0UdGcrjKrkN9QT6N0fFVEVcFJt3bXCFKiq6zdtKlYFJxoDL49NZ1GlawLRVYtozfhUFwFA76pi" + - "vvyFvXnpjWVUlcU4aP8auuI6hovPOQxVRUQAMOE4WFC2MmEI9YaiUUJ0X0F9yKyGxIuW3AZA+DgF" + - "v61ZnPJKRQEFL9FS3k3BFAjq4uWCqkAoiFormvdF6ZoKRFcupjnt8XfhUIDLJx3BN48/mMqyGPFY" + - "iF1jWyyqis21E6iGyF5CdD8hMkQI9gYJCFgAiB6oaN7Q8LAFYQeay6iJRFQFVMHx8+HuC6ZAsCoA" + - "iICqKICyS6H1S9mcaEf7Fm1bIYJBl9qacrqm4DWguTWgafDbIL8O0u9R/qWn6HGEgxMTAFC2soAB" + - "P6G0zrS0PKEggPIxqqBWQURQUO3mE3cF4uG6nirYnAeOYzGOURB2wSTb8NavJrNPLyIBh11jayTN" + - "v0TbHgevETQHeKAWALcYQEDZSkBEyayDtlmWttlKvpGthE8WDInN5nRbLMZ43RdMgS/hWEqh3E+m" + - "RNygEgqqtrNrCsFlC2g79OBdB6OKpl5G10+C7CpAQYRtRPgYB/x2JTlfScxSUksUzW4XirIDtWDi" + - "ZeolWrEWACQUaeuuYApEck5JeTNKX789gRhHnJJS8pvXIkKX1ED0w3m0ZM+muoguaXYxWj8R/CYQ" + - "AQSskmsCJw5OVEDA71BSi5S217b+9FOg2/ekXUcc6NmX/MZ1YFUQcGJFm7ormAIh41b1Wm+VAzXZ" + - "gteR0GDNYNJL39cthF0IL1tIUzIPFXStcy74jSAGAFWl/lpLxzuKBMCJAgb8JKgHOHyMKv8QMUZD" + - "g4aQnPMiKoCIOqU9VnZbMAWSD9UN+QDlWJvJSeYv7xMeOpzEzD8h7Fpw43Kam5rw+xXjGGGnIsPB" + - "REHTgGDTkF6tqANY8JJsgwEUAJSPPL0EULoWjGmgujfp5R8KgImVtG0JZhWAoVsUlIz/2jtqRUGl" + - "8903NDb8EMSEUNjlmM40/pplpHIeXZHwUKTHZMAFwIkJ1acZghWAgNqPjAIGnDhE66DHl4Wacw0D" + - "LjGE+8FOP7VQcCur1cSKNbe+XhSIjfjCMhONd+cepiBYO/hdU1TW6idbyjvemWuqzv2JBqr62OzG" + - "FQ67oh7BD9+l/YjDKA4H2CkJID0vJ1OfQJvvI1QjlI8zFB0sZJYr2U3gd4I44JZAsEoI9gS3FCQo" + - "CEpmDXgZ2PnLftkS+xc0/eH7+Ml2wUB05Ji54jipbgymwEQi6yNDhi1Mvv3KYdk1SyW3ZqUWjz3G" + - "Njw81QgqdEFVCS9ZQFPGUlNC10yUxBt9aLjXEttHKB4txIcKsf3lb+GgoApYthLAQm6j0vqK0vSs" + - "Jd8CIjuPsnjcMdoy7TeiqBjj+LERh7wIaDcGUyCO27klkGc7tgSDlzctT/7eVpx8Ng2/uwfVHLsS" + - "Wv0+ifYUWhVBROiKWh8vBe3v6t/GhCHYE6IDhUidEKoGEwIvCZl6SP1F6Vyh+B2AbB1lRyiEB+zl" + - "B/v0p+PtOQaBQJ8BqyN77/c2QDcHU1AybsLTm35184Vec0NVYsbjUn3uj6Ro9OFe++szAghdcho3" + - "0LlpI7naHoRcoStueSXKNvgZSK+GzlWKiO74ASMg0vV7LwCqRstPPlsTzz2Gl2wTMVB82DHPumXl" + - "mwvXfewB6vvO6h+c/mDLE787Ra1or8mXeMWHHcmHJx3uiPiGLqgE2XTlg3z+xK9THg3SlbZZM1h+" + - "1gTApzsFq+u8QQ8+ydKTxomX2OSYaFHH4N++OD42YvTcPbDCFIjj+JWnn3tX2ysvTMgnmoo3P3CH" + - "6XHyWfT46kS/6YmHBFTYCdEcgSXvksh+lfIoXQrVDsKUVOIlNrGdrhaRrlmjvS66yjb+7n7JNW9y" + - "cUR7njFlRmz4qPl78H6YgtiBo96s/t4lz6iKesmEs/6Gy2yvC66QQGU/q12djbEQWrqI5lSOXa8E" + - "fQgP2ptP+n1N8SCpoPPPnbBT0dIj/icfrhssmx+611GBQGXftupvnX8bIvk9G0xhlfGqTv/2jZEB" + - "+zQAND89zU0teFv7Xn6TlUDUdtEMwbVLaG9N4FslmW+gKbOGjN+5wzFNE45QPGY8WFAAC4niEHdM" + - "GMjJU0bw4Ji+GPsP9qIQqq6zfS6+Rtb85HzRXMqAY/v+6PpH3PKKN9mOc+WVV9K9CiQQ3Bzdd1iw" + - "afrDX1LNO8m359LzrO+pW1yh7W+/blAr7AjJWzoOPZaaAX2Yu/lWHls1ldc2z2VjOklJsILiQBwR" + - "wVefXDRAy1N/gnyWv4yu4s4zhzCztox2DAIctaABlF1y4mW29md32y2bdJqfneYCUnzI4cv6XnrD" + - "d8SYxKd1e0OBaqz+yose23j/z8cBFA3/gjfw9l/Lxjt+rg2P/soFX9iBQ+OP7mTUWWeyoOkaXtv0" + - "KqtTsDxpSfoVfLn34YzoU8bsxnksb23EeWMxxwRyvDGigqVJWJ5U2vLQvznNA3cuIJLz6YqEiuyA" + - "a27x1fOov+J8x+bTxo2Xdw6btfDUYK8+j32aN1AViKT6/eS6ye1zXn45tWR+Tce7r7v1V/zQ73/N" + - "L0R9z2+Y9oCzQzTWx/1wEa1pH8SwlWDE0JBp5oHVv2eB+jQnhdaUoWNQnIE1LmQUUP4uHzDkHEOY" + - "nQSjYCJFtt9lN/kmFmflxZMdm0sbxbGDpj50+5ZYngT49IMpPJqW7TP9pVPf/fy+T3qJTcUtM59y" + - "FPEGXHuLOOUV3oZ7fuGieeEjgsvfo7WjE9cN8FECOI5gEEQEgJyFVF7ZnhXBIqiyA1UIlFb5tdff" + - "ZlFY+aMpjt/ebFSh/yU/nV467pgrAf/fdItmgVtS9uqwF98620TK0mCl5aUn3OWTT6dq4tky8Of3" + - "eSZSZlXZJrC+nmRTC0aibE/4OFVFAWv4GMcqxirbUysaG3yAN+S3T2i+sYHlF37H8doajSr0Ovv7" + - "s/qce+E5QPbffO1qQah33+kH/nnhaYHKfq2qKm3vvOYu/to43LIKhr0415aOOTpvNaBWwSSayNav" + - "QrR0hzhcP86g6H4MjNUyuuJArjrwO9w06hGOesWl3+oOgr5iBEpSecJZH2vZOiqKG7N9Jl3k7f2b" + - "P7Hp/+7RlZed7/rpdqM4ts+5lz5be+2txyHS/hm62Lkg39x05AenfOWejoVv9hdUkIBWTzzHqznv" + - "YumYN1fX//JnJvXBItNy7k8lftpgZm28iRVJZXM2yoiKcXx3yERqi3qxvaY/Pcqyb09kc0WQRf3i" + - "lKY8Rq5IYBF1wnFKDxtva6ZcaHONTdRffZF0Ll/iYsAEI/m6a29/qPq0b56/LZbPVjAFNpMeuvrK" + - "i2/f+ODdY9TmHXwI1dT6vSedpz3GHyvJhfN1VUMSjhljFrb/UuLBfeRzPY+hX7w/O2PzORYePYbk" + - "orcQFRXXJVBdo+Vjj7QVx5+MuAHZcPdt2vTsYw54gkKopq55yN2/vano4M/dBmQBPqvBFKiWtc56" + - "4YJlF3x3Unb96nIEUKOR2sG28usnafmErxOoHUwwGkLEiCDCNgg70paXnmPNjVdr0fCRWjJmLOEB" + - "daRXraDxj7+j9dUXjc2kBFTEuH7VSWfOrbvqpkvc0rI/Awrw2Q+mwPgdHaPX3X3rj9dNvfEom0kF" + - "VAEVdYvLtGjoAVo85ggtGf05CfcbqMGqKjGhMB9pRwEBUN/Ha23R9OrlZFatlMRrL2v73NclXb/C" + - "qJ8XMQCyJaZD1g687hdTi0aMvh+Rlv/AL9gq0Hw+3PbWnMPX3n7jlLY5s8baXDYEgIIiagIh3NIe" + - "Gqqq1EBVb9zyCtxoXDFGbT5n/PaE5ho2mtzmjeSbW/A720R9X8SwTbimf33Pb5zxUO9vTv5VoKKq" + - "/r/gK/wKbDYTTi1eNHTzH393SvPzT0/IrF5Zp2KNCFtpF8cqBba/ndVEYqmKCcfP6Xn8xEeLRx78" + - "rFtS2oCIAvx3BVMgms/H8q3N+zc9/cTYphlPf/6vIWU3ru+jnufySUTULSpujwzca9mWPcy8skMP" + - "e6Xkc4fODlb32iyOk6cb/T/N+faHj8AX2gAAAABJRU5ErkJggg==" - } -}; - -// This global tracks if the page has been set up before, to prevent double inits -var gInitialized = false; -var gObserver = new MutationObserver(function (mutations) { - for (let mutation of mutations) { - if (mutation.attributeName == "searchEngineURL") { - setupSearchEngine(); - if (!gInitialized) { - gInitialized = true; - } - return; - } - } -}); - -window.addEventListener("pageshow", function () { - // Delay search engine setup, cause browser.js::BrowserOnAboutPageLoad runs - // later and may use asynchronous getters. - window.gObserver.observe(document.documentElement, { attributes: true }); - fitToWidth(); - window.addEventListener("resize", fitToWidth); -}); - -window.addEventListener("pagehide", function() { - window.gObserver.disconnect(); - window.removeEventListener("resize", fitToWidth); -}); - -function onSearchSubmit(aEvent) -{ - let searchTerms = document.getElementById("searchText").value; - let searchURL = document.documentElement.getAttribute("searchEngineURL"); - - if (searchURL && searchTerms.length > 0) { - // Send an event that a search was performed. This was originally - // added so Firefox Health Report could record that a search from - // about:home had occurred. - let engineName = document.documentElement.getAttribute("searchEngineName"); - let event = new CustomEvent("AboutHomeSearchEvent", {detail: engineName}); - document.dispatchEvent(event); - - const SEARCH_TOKEN = "_searchTerms_"; - let searchPostData = document.documentElement.getAttribute("searchEnginePostData"); - if (searchPostData) { - // Check if a post form already exists. If so, remove it. - const POST_FORM_NAME = "searchFormPost"; - let form = document.forms[POST_FORM_NAME]; - if (form) { - form.parentNode.removeChild(form); - } - - // Create a new post form. - form = document.body.appendChild(document.createElement("form")); - form.setAttribute("name", POST_FORM_NAME); - // Set the URL to submit the form to. - form.setAttribute("action", searchURL.replace(SEARCH_TOKEN, searchTerms)); - form.setAttribute("method", "post"); - - // Create new <input type=hidden> elements for search param. - searchPostData = searchPostData.split("&"); - for (let postVar of searchPostData) { - let [name, value] = postVar.split("="); - if (value == SEARCH_TOKEN) { - value = searchTerms; - } - let input = document.createElement("input"); - input.setAttribute("type", "hidden"); - input.setAttribute("name", name); - input.setAttribute("value", value); - form.appendChild(input); - } - // Submit the form. - form.submit(); - } else { - searchURL = searchURL.replace(SEARCH_TOKEN, encodeURIComponent(searchTerms)); - window.location.href = searchURL; - } - } - - aEvent.preventDefault(); -} - - -function setupSearchEngine() -{ - // The "autofocus" attribute doesn't focus the form element - // immediately when the element is first drawn, so the - // attribute is also used for styling when the page first loads. - let searchText = document.getElementById("searchText"); - searchText.addEventListener("blur", function searchText_onBlur() { - searchText.removeEventListener("blur", searchText_onBlur); - searchText.removeAttribute("autofocus"); - }); - - let searchEngineName = document.documentElement.getAttribute("searchEngineName"); - let searchEngineInfo = SEARCH_ENGINES[searchEngineName]; - let logoElt = document.getElementById("searchEngineLogo"); - - // Add search engine logo. - if (searchEngineInfo && searchEngineInfo.image) { - logoElt.parentNode.hidden = false; - logoElt.src = searchEngineInfo.image; - logoElt.alt = searchEngineName; - searchText.placeholder = ""; - } - else { - logoElt.parentNode.hidden = true; - searchText.placeholder = searchEngineName; - } - -} - -function fitToWidth() { - if (window.scrollMaxX) { - document.body.setAttribute("narrow", "true"); - } else if (document.body.hasAttribute("narrow")) { - document.body.removeAttribute("narrow"); - fitToWidth(); - } -} diff --git a/components/abouthome/aboutHome.xhtml b/components/abouthome/aboutHome.xhtml deleted file mode 100644 index d72ec49..0000000 --- a/components/abouthome/aboutHome.xhtml +++ /dev/null @@ -1,62 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<!DOCTYPE html [ - <!ENTITY % htmlDTD - PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" - "DTD/xhtml1-strict.dtd"> - %htmlDTD; - <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd"> - %globalDTD; - <!ENTITY % aboutHomeDTD SYSTEM "chrome://browser/locale/aboutHome.dtd"> - %aboutHomeDTD; - <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd" > - %browserDTD; -]> - -<html xmlns="http://www.w3.org/1999/xhtml"> - <head> - <title>&abouthome.pageTitle;</title> - - <link rel="icon" type="image/png" id="favicon" - href="chrome://branding/content/icon32.png"/> - <link rel="stylesheet" type="text/css" media="all" - href="chrome://browser/content/abouthome/aboutHome.css"/> - - <script type="text/javascript;version=1.8" - src="chrome://browser/content/abouthome/aboutHome.js"/> - </head> - - <body dir="&locale.dir;"> - <div class="spacer"/> - <div id="topSection"> - <div id="brandLogo"></div> - - <div id="searchContainer"> - <form name="searchForm" id="searchForm" onsubmit="onSearchSubmit(event)"> - <div id="searchLogoContainer"><img id="searchEngineLogo"/></div> - <input type="text" name="q" value="" id="searchText" maxlength="256" - autofocus="autofocus"/> - <input id="searchSubmit" type="submit" value="&abouthome.searchEngineButton.label;"/> - </form> - </div> - </div> - <div class="spacer"/> - - <div id="launcher"> - <button class="launchButton" id="downloads">&abouthome.downloadsButton.label;</button> - <button class="launchButton" id="bookmarks">&abouthome.bookmarksButton.label;</button> - <button class="launchButton" id="history">&abouthome.historyButton.label;</button> - <button class="launchButton" id="addons">&abouthome.addonsButton.label;</button> -#ifdef MOZ_SERVICES_SYNC - <button class="launchButton" id="sync">&abouthome.syncButton.label;</button> -#endif - <button class="launchButton" id="settings">&abouthome.settingsButton.label;</button> - <div id="restorePreviousSessionSeparator"/> - <button class="launchButton" id="restorePreviousSession">&historyRestoreLastSession.label;</button> - </div> - </body> -</html> diff --git a/components/abouthome/addons.png b/components/abouthome/addons.png Binary files differdeleted file mode 100644 index 41519ce..0000000 --- a/components/abouthome/addons.png +++ /dev/null diff --git a/components/abouthome/addons@2x.png b/components/abouthome/addons@2x.png Binary files differdeleted file mode 100644 index d4d04ee..0000000 --- a/components/abouthome/addons@2x.png +++ /dev/null diff --git a/components/abouthome/bookmarks.png b/components/abouthome/bookmarks.png Binary files differdeleted file mode 100644 index 5c7e194..0000000 --- a/components/abouthome/bookmarks.png +++ /dev/null diff --git a/components/abouthome/bookmarks@2x.png b/components/abouthome/bookmarks@2x.png Binary files differdeleted file mode 100644 index 7ede007..0000000 --- a/components/abouthome/bookmarks@2x.png +++ /dev/null diff --git a/components/abouthome/downloads.png b/components/abouthome/downloads.png Binary files differdeleted file mode 100644 index 3d4d10e..0000000 --- a/components/abouthome/downloads.png +++ /dev/null diff --git a/components/abouthome/downloads@2x.png b/components/abouthome/downloads@2x.png Binary files differdeleted file mode 100644 index d384a22..0000000 --- a/components/abouthome/downloads@2x.png +++ /dev/null diff --git a/components/abouthome/history.png b/components/abouthome/history.png Binary files differdeleted file mode 100644 index ae742b1..0000000 --- a/components/abouthome/history.png +++ /dev/null diff --git a/components/abouthome/history@2x.png b/components/abouthome/history@2x.png Binary files differdeleted file mode 100644 index 696902e..0000000 --- a/components/abouthome/history@2x.png +++ /dev/null diff --git a/components/abouthome/jar.mn b/components/abouthome/jar.mn deleted file mode 100644 index e1ae4ac..0000000 --- a/components/abouthome/jar.mn +++ /dev/null @@ -1,33 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -browser.jar: -* content/browser/abouthome/aboutHome.xhtml - content/browser/abouthome/aboutHome.js -* content/browser/abouthome/aboutHome.css - content/browser/abouthome/noise.png - content/browser/abouthome/snippet1.png - content/browser/abouthome/snippet2.png - content/browser/abouthome/downloads.png - content/browser/abouthome/bookmarks.png - content/browser/abouthome/history.png - content/browser/abouthome/addons.png -#ifdef MOZ_SERVICES_SYNC - content/browser/abouthome/sync.png -#endif - content/browser/abouthome/settings.png - content/browser/abouthome/restore.png - content/browser/abouthome/restore-large.png - content/browser/abouthome/snippet1@2x.png - content/browser/abouthome/snippet2@2x.png - content/browser/abouthome/downloads@2x.png - content/browser/abouthome/bookmarks@2x.png - content/browser/abouthome/history@2x.png - content/browser/abouthome/addons@2x.png -#ifdef MOZ_SERVICES_SYNC - content/browser/abouthome/sync@2x.png -#endif - content/browser/abouthome/settings@2x.png - content/browser/abouthome/restore@2x.png - content/browser/abouthome/restore-large@2x.png
\ No newline at end of file diff --git a/components/abouthome/moz.build b/components/abouthome/moz.build deleted file mode 100644 index 2d64d50..0000000 --- a/components/abouthome/moz.build +++ /dev/null @@ -1,8 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -JAR_MANIFESTS += ['jar.mn'] - diff --git a/components/abouthome/noise.png b/components/abouthome/noise.png Binary files differdeleted file mode 100644 index 3467cf4..0000000 --- a/components/abouthome/noise.png +++ /dev/null diff --git a/components/abouthome/restore-large.png b/components/abouthome/restore-large.png Binary files differdeleted file mode 100644 index ef593e6..0000000 --- a/components/abouthome/restore-large.png +++ /dev/null diff --git a/components/abouthome/restore-large@2x.png b/components/abouthome/restore-large@2x.png Binary files differdeleted file mode 100644 index d5c71d0..0000000 --- a/components/abouthome/restore-large@2x.png +++ /dev/null diff --git a/components/abouthome/restore.png b/components/abouthome/restore.png Binary files differdeleted file mode 100644 index 5c3d6f4..0000000 --- a/components/abouthome/restore.png +++ /dev/null diff --git a/components/abouthome/restore@2x.png b/components/abouthome/restore@2x.png Binary files differdeleted file mode 100644 index 5acb630..0000000 --- a/components/abouthome/restore@2x.png +++ /dev/null diff --git a/components/abouthome/settings.png b/components/abouthome/settings.png Binary files differdeleted file mode 100644 index 4b0c309..0000000 --- a/components/abouthome/settings.png +++ /dev/null diff --git a/components/abouthome/settings@2x.png b/components/abouthome/settings@2x.png Binary files differdeleted file mode 100644 index c77cb9a..0000000 --- a/components/abouthome/settings@2x.png +++ /dev/null diff --git a/components/abouthome/snippet1.png b/components/abouthome/snippet1.png Binary files differdeleted file mode 100644 index ce2ec55..0000000 --- a/components/abouthome/snippet1.png +++ /dev/null diff --git a/components/abouthome/snippet1@2x.png b/components/abouthome/snippet1@2x.png Binary files differdeleted file mode 100644 index f57cd0a..0000000 --- a/components/abouthome/snippet1@2x.png +++ /dev/null diff --git a/components/abouthome/snippet2.png b/components/abouthome/snippet2.png Binary files differdeleted file mode 100644 index e0724fb..0000000 --- a/components/abouthome/snippet2.png +++ /dev/null diff --git a/components/abouthome/snippet2@2x.png b/components/abouthome/snippet2@2x.png Binary files differdeleted file mode 100644 index 40577f5..0000000 --- a/components/abouthome/snippet2@2x.png +++ /dev/null diff --git a/components/abouthome/sync.png b/components/abouthome/sync.png Binary files differdeleted file mode 100644 index 11e40cc..0000000 --- a/components/abouthome/sync.png +++ /dev/null diff --git a/components/abouthome/sync@2x.png b/components/abouthome/sync@2x.png Binary files differdeleted file mode 100644 index 6354f5b..0000000 --- a/components/abouthome/sync@2x.png +++ /dev/null diff --git a/components/build/Makefile.in b/components/build/Makefile.in deleted file mode 100644 index 2387227..0000000 --- a/components/build/Makefile.in +++ /dev/null @@ -1,8 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -include $(topsrcdir)/config/rules.mk - -# Ensure that we don't embed a manifest referencing the CRT. -EMBED_MANIFEST_AT = diff --git a/components/build/moz.build b/components/build/moz.build deleted file mode 100644 index ea1f771..0000000 --- a/components/build/moz.build +++ /dev/null @@ -1,36 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -EXPORTS += ['nsBrowserCompsCID.h'] - -SOURCES += ['nsModule.cpp'] - -XPCOMBinaryComponent('browsercomps') - -LOCAL_INCLUDES += [ - '../dirprovider', - '../feeds', - '../shell', -] - -if CONFIG['OS_ARCH'] == 'WINNT': - OS_LIBS += [ - 'esent', - 'netapi32', - 'ole32', - 'shell32', - 'shlwapi', - 'version', - ] - DELAYLOAD_DLLS += [ - 'esent.dll', - 'netapi32.dll', - ] - -# Mac: Need to link with CoreFoundation for Mac Migrators (PList reading code) -# GTK2: Need to link with glib for GNOME shell service -if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('cocoa', 'gtk2', 'gtk3'): - OS_LIBS += CONFIG['TK_LIBS'] diff --git a/components/build/nsBrowserCompsCID.h b/components/build/nsBrowserCompsCID.h deleted file mode 100644 index bbaa9ab..0000000 --- a/components/build/nsBrowserCompsCID.h +++ /dev/null @@ -1,31 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -///////////////////////////////////////////////////////////////////////////// - -#define NS_SHELLSERVICE_CID \ -{ 0x63c7b9f4, 0xcc8, 0x43f8, { 0xb6, 0x66, 0xa, 0x66, 0x16, 0x55, 0xcb, 0x73 } } - -#define NS_SHELLSERVICE_CONTRACTID \ - "@mozilla.org/browser/shell-service;1" - -#define NS_RDF_FORWARDPROXY_INFER_DATASOURCE_CID \ -{ 0x7a024bcf, 0xedd5, 0x4d9a, { 0x86, 0x14, 0xd4, 0x4b, 0xe1, 0xda, 0xda, 0xd3 } } - -#define NS_FEEDSNIFFER_CID \ -{ 0x6893e69, 0x71d8, 0x4b23, { 0x81, 0xeb, 0x80, 0x31, 0x4d, 0xaf, 0x3e, 0x66 } } - -#define NS_FEEDSNIFFER_CONTRACTID \ - "@mozilla.org/browser/feeds/sniffer;1" - -#define NS_ABOUTFEEDS_CID \ -{ 0x12ff56ec, 0x58be, 0x402c, { 0xb0, 0x57, 0x1, 0xf9, 0x61, 0xde, 0x96, 0x9b } } - -// 136e2c4d-c5a4-477c-b131-d93d7d704f64 -#define NS_PRIVATE_BROWSING_SERVICE_WRAPPER_CID \ -{ 0x136e2c4d, 0xc5a4, 0x477c, { 0xb1, 0x31, 0xd9, 0x3d, 0x7d, 0x70, 0x4f, 0x64 } } - -// {6DEB193C-F87D-4078-BC78-5E64655B4D62} -#define NS_BROWSERDIRECTORYPROVIDER_CID \ -{ 0x6deb193c, 0xf87d, 0x4078, { 0xbc, 0x78, 0x5e, 0x64, 0x65, 0x5b, 0x4d, 0x62 } } diff --git a/components/build/nsModule.cpp b/components/build/nsModule.cpp deleted file mode 100644 index f98fc08..0000000 --- a/components/build/nsModule.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "mozilla/ModuleUtils.h" - -#include "nsBrowserCompsCID.h" -#include "DirectoryProvider.h" - -#if defined(XP_WIN) -#include "nsWindowsShellService.h" -#elif defined(XP_MACOSX) -#include "nsMacShellService.h" -#elif defined(MOZ_WIDGET_GTK) -#include "nsGNOMEShellService.h" -#endif - -#include "rdf.h" -#include "nsFeedSniffer.h" - -#include "nsNetCID.h" - -using namespace mozilla::browser; - -///////////////////////////////////////////////////////////////////////////// - -NS_GENERIC_FACTORY_CONSTRUCTOR(DirectoryProvider) -#if defined(XP_WIN) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindowsShellService) -#elif defined(XP_MACOSX) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsMacShellService) -#elif defined(MOZ_WIDGET_GTK) -NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGNOMEShellService, Init) -#endif - -NS_GENERIC_FACTORY_CONSTRUCTOR(nsFeedSniffer) - -NS_DEFINE_NAMED_CID(NS_BROWSERDIRECTORYPROVIDER_CID); -#if defined(XP_WIN) -NS_DEFINE_NAMED_CID(NS_SHELLSERVICE_CID); -#elif defined(MOZ_WIDGET_GTK) -NS_DEFINE_NAMED_CID(NS_SHELLSERVICE_CID); -#endif -NS_DEFINE_NAMED_CID(NS_FEEDSNIFFER_CID); -#ifdef XP_MACOSX -NS_DEFINE_NAMED_CID(NS_SHELLSERVICE_CID); -#endif - -static const mozilla::Module::CIDEntry kBrowserCIDs[] = { - { &kNS_BROWSERDIRECTORYPROVIDER_CID, false, nullptr, DirectoryProviderConstructor }, -#if defined(XP_WIN) - { &kNS_SHELLSERVICE_CID, false, nullptr, nsWindowsShellServiceConstructor }, -#elif defined(MOZ_WIDGET_GTK) - { &kNS_SHELLSERVICE_CID, false, nullptr, nsGNOMEShellServiceConstructor }, -#endif - { &kNS_FEEDSNIFFER_CID, false, nullptr, nsFeedSnifferConstructor }, -#ifdef XP_MACOSX - { &kNS_SHELLSERVICE_CID, false, nullptr, nsMacShellServiceConstructor }, -#endif - { nullptr } -}; - -static const mozilla::Module::ContractIDEntry kBrowserContracts[] = { - { NS_BROWSERDIRECTORYPROVIDER_CONTRACTID, &kNS_BROWSERDIRECTORYPROVIDER_CID }, -#if defined(XP_WIN) - { NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID }, -#elif defined(MOZ_WIDGET_GTK) - { NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID }, -#endif - { NS_FEEDSNIFFER_CONTRACTID, &kNS_FEEDSNIFFER_CID }, -#ifdef XP_MACOSX - { NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID }, -#endif - { nullptr } -}; - -static const mozilla::Module::CategoryEntry kBrowserCategories[] = { - { XPCOM_DIRECTORY_PROVIDER_CATEGORY, "browser-directory-provider", NS_BROWSERDIRECTORYPROVIDER_CONTRACTID }, - { NS_CONTENT_SNIFFER_CATEGORY, "Feed Sniffer", NS_FEEDSNIFFER_CONTRACTID }, - { nullptr } -}; - -static const mozilla::Module kBrowserModule = { - mozilla::Module::kVersion, - kBrowserCIDs, - kBrowserContracts, - kBrowserCategories -}; - -NSMODULE_DEFN(nsBrowserCompsModule) = &kBrowserModule; diff --git a/components/certerror/content/aboutCertError.css b/components/certerror/content/aboutCertError.css deleted file mode 100644 index 059d812..0000000 --- a/components/certerror/content/aboutCertError.css +++ /dev/null @@ -1,17 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* Logical CSS rules belong here, but presentation & theming rules - should live in the CSS of the appropriate theme */ - -#technicalContentText { - overflow: auto; - white-space: pre-wrap; -} - -.expander[hidden], -.expander[hidden] + *, -.expander[collapsed] + * { - display: none; -} diff --git a/components/certerror/content/aboutCertError.xhtml b/components/certerror/content/aboutCertError.xhtml deleted file mode 100644 index c8a7e44..0000000 --- a/components/certerror/content/aboutCertError.xhtml +++ /dev/null @@ -1,247 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<!DOCTYPE html [ - <!ENTITY % htmlDTD - PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" - "DTD/xhtml1-strict.dtd"> - %htmlDTD; - <!ENTITY % globalDTD - SYSTEM "chrome://global/locale/global.dtd"> - %globalDTD; - <!ENTITY % certerrorDTD - SYSTEM "chrome://browser/locale/aboutCertError.dtd"> - %certerrorDTD; -]> - -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> -<html xmlns="http://www.w3.org/1999/xhtml"> - <head> - <title>&certerror.pagetitle;</title> - <link rel="stylesheet" href="chrome://browser/skin/aboutCertError.css" type="text/css" media="all" /> - <link rel="stylesheet" href="chrome://browser/content/certerror/aboutCertError.css" type="text/css" media="all" /> - <!-- This page currently uses the same favicon as neterror.xhtml. - If the location of the favicon is changed for both pages, the - FAVICON_ERRORPAGE_URL symbol in toolkit/components/places/src/nsFaviconService.h - should be updated. If this page starts using a different favicon - than neterror.xhtml nsFaviconService->SetAndLoadFaviconForPage - should be updated to ignore this one as well. --> - <link rel="icon" type="image/png" id="favicon" href="chrome://global/skin/icons/warning-16.png"/> - - <script type="application/javascript"><![CDATA[ - // Error url MUST be formatted like this: - // about:certerror?e=error&u=url&d=desc - - // Note that this file uses document.documentURI to get - // the URL (with the format from above). This is because - // document.location.href gets the current URI off the docshell, - // which is the URL displayed in the location bar, i.e. - // the URI that the user attempted to load. - - function getCSSClass() - { - var url = document.documentURI; - var matches = url.match(/s\=([^&]+)\&/); - // s is optional, if no match just return nothing - if (!matches || matches.length < 2) - return ""; - - // parenthetical match is the second entry - return decodeURIComponent(matches[1]); - } - - function getDescription() - { - var url = document.documentURI; - var desc = url.search(/d\=/); - - // desc == -1 if not found; if so, return an empty string - // instead of what would turn out to be portions of the URI - if (desc == -1) - return ""; - - return decodeURIComponent(url.slice(desc + 2)); - } - - function initPage() - { - // Replace the "#1" string in the intro with the hostname. Trickier - // than it might seem since we want to preserve the <b> tags, but - // not allow for any injection by just using innerHTML. Instead, - // just find the right target text node. - var intro = document.getElementById('introContentP1'); - function replaceWithHost(node) { - if (node.textContent == "#1") - node.textContent = location.host; - else - for(var i = 0; i < node.childNodes.length; i++) - replaceWithHost(node.childNodes[i]); - }; - replaceWithHost(intro); - - if (getCSSClass() == "expertBadCert") { - toggle('technicalContent'); - toggle('expertContent'); - } - - // Disallow overrides if this is a Strict-Transport-Security - // host and the cert is bad (STS Spec section 7.3) or if the - // certerror is in a frame (bug 633691). - if (getCSSClass() == "badStsCert" || window != top) - document.getElementById("expertContent").setAttribute("hidden", "true"); - - var tech = document.getElementById("technicalContentText"); - if (tech) - tech.textContent = getDescription(); - - addDomainErrorLink(); - } - - /* In the case of SSL error pages about domain mismatch, see if - we can hyperlink the user to the correct site. We don't want - to do this generically since it allows MitM attacks to redirect - users to a site under attacker control, but in certain cases - it is safe (and helpful!) to do so. Bug 402210 - */ - function addDomainErrorLink() { - // Rather than textContent, we need to treat description as HTML - var sd = document.getElementById("technicalContentText"); - if (sd) { - var desc = getDescription(); - - // sanitize description text - see bug 441169 - - // First, find the index of the <a> tag we care about, being careful not to - // use an over-greedy regex - var re = /<a id="cert_domain_link" title="([^"]+)">/; - var result = re.exec(desc); - if(!result) - return; - - // Remove sd's existing children - sd.textContent = ""; - - // Everything up to the link should be text content - sd.appendChild(document.createTextNode(desc.slice(0, result.index))); - - // Now create the link itself - var anchorEl = document.createElement("a"); - anchorEl.setAttribute("id", "cert_domain_link"); - anchorEl.setAttribute("title", result[1]); - anchorEl.appendChild(document.createTextNode(result[1])); - sd.appendChild(anchorEl); - - // Finally, append text for anything after the closing </a> - sd.appendChild(document.createTextNode(desc.slice(desc.indexOf("</a>") + "</a>".length))); - } - - var link = document.getElementById('cert_domain_link'); - if (!link) - return; - - var okHost = link.getAttribute("title"); - var thisHost = document.location.hostname; - var proto = document.location.protocol; - - // If okHost is a wildcard domain ("*.example.com") let's - // use "www" instead. "*.example.com" isn't going to - // get anyone anywhere useful. bug 432491 - okHost = okHost.replace(/^\*\./, "www."); - - /* case #1: - * example.com uses an invalid security certificate. - * - * The certificate is only valid for www.example.com - * - * Make sure to include the "." ahead of thisHost so that - * a MitM attack on paypal.com doesn't hyperlink to "notpaypal.com" - * - * We'd normally just use a RegExp here except that we lack a - * library function to escape them properly (bug 248062), and - * domain names are famous for having '.' characters in them, - * which would allow spurious and possibly hostile matches. - */ - if (endsWith(okHost, "." + thisHost)) - link.href = proto + okHost; - - /* case #2: - * browser.garage.maemo.org uses an invalid security certificate. - * - * The certificate is only valid for garage.maemo.org - */ - if (endsWith(thisHost, "." + okHost)) - link.href = proto + okHost; - - // If we set a link, meaning there's something helpful for - // the user here, expand the section by default - if (link.href && getCSSClass() != "expertBadCert") - toggle("technicalContent"); - } - - function endsWith(haystack, needle) { - return haystack.slice(-needle.length) == needle; - } - - function toggle(id) { - var el = document.getElementById(id); - if (el.getAttribute("collapsed")) - el.removeAttribute("collapsed"); - else - el.setAttribute("collapsed", true); - } - ]]></script> - </head> - - <body dir="&locale.dir;"> - - <!-- PAGE CONTAINER (for styling purposes only) --> - <div id="errorPageContainer"> - - <!-- Error Title --> - <div id="errorTitle"> - <h1 id="errorTitleText">&certerror.longpagetitle;</h1> - </div> - - <!-- LONG CONTENT (the section most likely to require scrolling) --> - <div id="errorLongContent"> - <div id="introContent"> - <p id="introContentP1">&certerror.introPara1;</p> - <p>&certerror.introPara2;</p> - </div> - - <div id="whatShouldIDoContent"> - <h2>&certerror.whatShouldIDo.heading;</h2> - <div id="whatShouldIDoContentText"> - <p>&certerror.whatShouldIDo.content;</p> - <button id='getMeOutOfHereButton'>&certerror.getMeOutOfHere.label;</button> - </div> - </div> - - <!-- The following sections can be unhidden by default by setting the - "browser.xul.error_pages.expert_bad_cert" pref to true --> - <h2 id="technicalContent" class="expander" collapsed="true"> - <button onclick="toggle('technicalContent');">&certerror.technical.heading;</button> - </h2> - <p id="technicalContentText"/> - - <h2 id="expertContent" class="expander" collapsed="true"> - <button onclick="toggle('expertContent');">&certerror.expert.heading;</button> - </h2> - <div> - <p>&certerror.expert.content;</p> - <p>&certerror.expert.contentPara2;</p> - <button id='exceptionDialogButton'>&certerror.addException.label;</button> - </div> - </div> - </div> - - <!-- - - Note: It is important to run the script this way, instead of using - - an onload handler. This is because error pages are loaded as - - LOAD_BACKGROUND, which means that onload handlers will not be executed. - --> - <script type="application/javascript">initPage();</script> - - </body> -</html> diff --git a/components/certerror/jar.mn b/components/certerror/jar.mn deleted file mode 100644 index 08e0710..0000000 --- a/components/certerror/jar.mn +++ /dev/null @@ -1,7 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -browser.jar: - content/browser/certerror/aboutCertError.xhtml (content/aboutCertError.xhtml) - content/browser/certerror/aboutCertError.css (content/aboutCertError.css) diff --git a/components/certerror/moz.build b/components/certerror/moz.build deleted file mode 100644 index c97072b..0000000 --- a/components/certerror/moz.build +++ /dev/null @@ -1,7 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -JAR_MANIFESTS += ['jar.mn']
\ No newline at end of file diff --git a/components/dirprovider/DirectoryProvider.cpp b/components/dirprovider/DirectoryProvider.cpp deleted file mode 100644 index 85728b3..0000000 --- a/components/dirprovider/DirectoryProvider.cpp +++ /dev/null @@ -1,268 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsIDirectoryService.h" -#include "DirectoryProvider.h" - -#include "nsIFile.h" -#include "nsISimpleEnumerator.h" -#include "nsIPrefService.h" -#include "nsIPrefBranch.h" - -#include "nsArrayEnumerator.h" -#include "nsEnumeratorUtils.h" -#include "nsAppDirectoryServiceDefs.h" -#include "nsDirectoryServiceDefs.h" -#include "nsCategoryManagerUtils.h" -#include "nsComponentManagerUtils.h" -#include "nsCOMArray.h" -#include "nsDirectoryServiceUtils.h" -#include "mozilla/ModuleUtils.h" -#include "nsServiceManagerUtils.h" -#include "nsStringAPI.h" -#include "nsXULAppAPI.h" -#include "nsIPrefLocalizedString.h" - -namespace mozilla { -namespace browser { - -NS_IMPL_ISUPPORTS(DirectoryProvider, - nsIDirectoryServiceProvider, - nsIDirectoryServiceProvider2) - -NS_IMETHODIMP -DirectoryProvider::GetFile(const char *aKey, bool *aPersist, nsIFile* *aResult) -{ - return NS_ERROR_FAILURE; -} - -static void -AppendFileKey(const char *key, nsIProperties* aDirSvc, - nsCOMArray<nsIFile> &array) -{ - nsCOMPtr<nsIFile> file; - nsresult rv = aDirSvc->Get(key, NS_GET_IID(nsIFile), getter_AddRefs(file)); - if (NS_FAILED(rv)) - return; - - bool exists; - rv = file->Exists(&exists); - if (NS_FAILED(rv) || !exists) - return; - - array.AppendObject(file); -} - -// Appends the distribution-specific search engine directories to the -// array. The directory structure is as follows: - -// appdir/ -// \- distribution/ -// \- searchplugins/ -// |- common/ -// \- locale/ -// |- <locale 1>/ -// ... -// \- <locale N>/ - -// common engines are loaded for all locales. If there is no locale -// directory for the current locale, there is a pref: -// "distribution.searchplugins.defaultLocale" -// which specifies a default locale to use. - -static void -AppendDistroSearchDirs(nsIProperties* aDirSvc, nsCOMArray<nsIFile> &array) -{ - nsCOMPtr<nsIFile> searchPlugins; - nsresult rv = aDirSvc->Get(XRE_APP_DISTRIBUTION_DIR, - NS_GET_IID(nsIFile), - getter_AddRefs(searchPlugins)); - if (NS_FAILED(rv)) - return; - searchPlugins->AppendNative(NS_LITERAL_CSTRING("searchplugins")); - - bool exists; - rv = searchPlugins->Exists(&exists); - if (NS_FAILED(rv) || !exists) - return; - - nsCOMPtr<nsIFile> commonPlugins; - rv = searchPlugins->Clone(getter_AddRefs(commonPlugins)); - if (NS_SUCCEEDED(rv)) { - commonPlugins->AppendNative(NS_LITERAL_CSTRING("common")); - rv = commonPlugins->Exists(&exists); - if (NS_SUCCEEDED(rv) && exists) - array.AppendObject(commonPlugins); - } - - nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID)); - if (prefs) { - - nsCOMPtr<nsIFile> localePlugins; - rv = searchPlugins->Clone(getter_AddRefs(localePlugins)); - if (NS_FAILED(rv)) - return; - - localePlugins->AppendNative(NS_LITERAL_CSTRING("locale")); - - nsCString locale; - nsCOMPtr<nsIPrefLocalizedString> prefString; - rv = prefs->GetComplexValue("general.useragent.locale", - NS_GET_IID(nsIPrefLocalizedString), - getter_AddRefs(prefString)); - if (NS_SUCCEEDED(rv)) { - nsAutoString wLocale; - prefString->GetData(getter_Copies(wLocale)); - CopyUTF16toUTF8(wLocale, locale); - } else { - rv = prefs->GetCharPref("general.useragent.locale", getter_Copies(locale)); - } - - if (NS_SUCCEEDED(rv)) { - - nsCOMPtr<nsIFile> curLocalePlugins; - rv = localePlugins->Clone(getter_AddRefs(curLocalePlugins)); - if (NS_SUCCEEDED(rv)) { - - curLocalePlugins->AppendNative(locale); - rv = curLocalePlugins->Exists(&exists); - if (NS_SUCCEEDED(rv) && exists) { - array.AppendObject(curLocalePlugins); - return; // all done - } - } - } - - // we didn't append the locale dir - try the default one - nsCString defLocale; - rv = prefs->GetCharPref("distribution.searchplugins.defaultLocale", - getter_Copies(defLocale)); - if (NS_SUCCEEDED(rv)) { - - nsCOMPtr<nsIFile> defLocalePlugins; - rv = localePlugins->Clone(getter_AddRefs(defLocalePlugins)); - if (NS_SUCCEEDED(rv)) { - - defLocalePlugins->AppendNative(defLocale); - rv = defLocalePlugins->Exists(&exists); - if (NS_SUCCEEDED(rv) && exists) - array.AppendObject(defLocalePlugins); - } - } - } -} - -NS_IMETHODIMP -DirectoryProvider::GetFiles(const char *aKey, nsISimpleEnumerator* *aResult) -{ - nsresult rv; - - if (!strcmp(aKey, NS_APP_SEARCH_DIR_LIST)) { - nsCOMPtr<nsIProperties> dirSvc - (do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID)); - if (!dirSvc) - return NS_ERROR_FAILURE; - - nsCOMArray<nsIFile> baseFiles; - - /** - * We want to preserve the following order, since the search service loads - * engines in first-loaded-wins order. - * - extension search plugin locations (prepended below using - * NS_NewUnionEnumerator) - * - distro search plugin locations - * - user search plugin locations (profile) - * - app search plugin location (shipped engines) - */ - AppendDistroSearchDirs(dirSvc, baseFiles); - AppendFileKey(NS_APP_USER_SEARCH_DIR, dirSvc, baseFiles); - AppendFileKey(NS_APP_SEARCH_DIR, dirSvc, baseFiles); - - nsCOMPtr<nsISimpleEnumerator> baseEnum; - rv = NS_NewArrayEnumerator(getter_AddRefs(baseEnum), baseFiles); - if (NS_FAILED(rv)) - return rv; - - nsCOMPtr<nsISimpleEnumerator> list; - rv = dirSvc->Get(XRE_EXTENSIONS_DIR_LIST, - NS_GET_IID(nsISimpleEnumerator), getter_AddRefs(list)); - if (NS_FAILED(rv)) - return rv; - - static char const *const kAppendSPlugins[] = {"searchplugins", nullptr}; - - nsCOMPtr<nsISimpleEnumerator> extEnum = - new AppendingEnumerator(list, kAppendSPlugins); - if (!extEnum) - return NS_ERROR_OUT_OF_MEMORY; - - return NS_NewUnionEnumerator(aResult, extEnum, baseEnum); - } - - return NS_ERROR_FAILURE; -} - -NS_IMPL_ISUPPORTS(DirectoryProvider::AppendingEnumerator, nsISimpleEnumerator) - -NS_IMETHODIMP -DirectoryProvider::AppendingEnumerator::HasMoreElements(bool *aResult) -{ - *aResult = mNext ? true : false; - return NS_OK; -} - -NS_IMETHODIMP -DirectoryProvider::AppendingEnumerator::GetNext(nsISupports* *aResult) -{ - if (aResult) - NS_ADDREF(*aResult = mNext); - - mNext = nullptr; - - nsresult rv; - - // Ignore all errors - - bool more; - while (NS_SUCCEEDED(mBase->HasMoreElements(&more)) && more) { - nsCOMPtr<nsISupports> nextbasesupp; - mBase->GetNext(getter_AddRefs(nextbasesupp)); - - nsCOMPtr<nsIFile> nextbase(do_QueryInterface(nextbasesupp)); - if (!nextbase) - continue; - - nextbase->Clone(getter_AddRefs(mNext)); - if (!mNext) - continue; - - char const *const * i = mAppendList; - while (*i) { - mNext->AppendNative(nsDependentCString(*i)); - ++i; - } - - bool exists; - rv = mNext->Exists(&exists); - if (NS_SUCCEEDED(rv) && exists) - break; - - mNext = nullptr; - } - - return NS_OK; -} - -DirectoryProvider::AppendingEnumerator::AppendingEnumerator - (nsISimpleEnumerator* aBase, - char const *const *aAppendList) : - mBase(aBase), - mAppendList(aAppendList) -{ - // Initialize mNext to begin. - GetNext(nullptr); -} - -} // namespace browser -} // namespace mozilla diff --git a/components/dirprovider/DirectoryProvider.h b/components/dirprovider/DirectoryProvider.h deleted file mode 100644 index 43fa85a..0000000 --- a/components/dirprovider/DirectoryProvider.h +++ /dev/null @@ -1,51 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef DirectoryProvider_h__ -#define DirectoryProvider_h__ - -#include "nsIDirectoryService.h" -#include "nsComponentManagerUtils.h" -#include "nsISimpleEnumerator.h" -#include "nsIFile.h" -#include "mozilla/Attributes.h" - -#define NS_BROWSERDIRECTORYPROVIDER_CONTRACTID \ - "@mozilla.org/browser/directory-provider;1" - -namespace mozilla { -namespace browser { - -class DirectoryProvider final : public nsIDirectoryServiceProvider2 -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIDIRECTORYSERVICEPROVIDER - NS_DECL_NSIDIRECTORYSERVICEPROVIDER2 - -private: - ~DirectoryProvider() {} - - class AppendingEnumerator final : public nsISimpleEnumerator - { - public: - NS_DECL_ISUPPORTS - NS_DECL_NSISIMPLEENUMERATOR - - AppendingEnumerator(nsISimpleEnumerator* aBase, - char const *const *aAppendList); - - private: - ~AppendingEnumerator() {} - - nsCOMPtr<nsISimpleEnumerator> mBase; - char const *const *const mAppendList; - nsCOMPtr<nsIFile> mNext; - }; -}; - -} // namespace browser -} // namespace mozilla - -#endif // DirectoryProvider_h__ diff --git a/components/dirprovider/moz.build b/components/dirprovider/moz.build deleted file mode 100644 index b01c4a3..0000000 --- a/components/dirprovider/moz.build +++ /dev/null @@ -1,13 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -EXPORTS.mozilla.browser += ['DirectoryProvider.h'] - -SOURCES += ['DirectoryProvider.cpp'] - -FINAL_LIBRARY = 'browsercomps' - -LOCAL_INCLUDES += ['../build'] diff --git a/components/distribution.js b/components/distribution.js deleted file mode 100644 index 121e55b..0000000 --- a/components/distribution.js +++ /dev/null @@ -1,345 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -this.EXPORTED_SYMBOLS = [ "DistributionCustomizer" ]; - -var Ci = Components.interfaces; -var Cc = Components.classes; -var Cr = Components.results; -var Cu = Components.utils; - -const DISTRIBUTION_CUSTOMIZATION_COMPLETE_TOPIC = - "distribution-customization-complete"; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", - "resource://gre/modules/PlacesUtils.jsm"); - -this.DistributionCustomizer = function DistributionCustomizer() { - let dirSvc = Cc["@mozilla.org/file/directory_service;1"]. - getService(Ci.nsIProperties); - let iniFile = dirSvc.get("XREExeF", Ci.nsIFile); - iniFile.leafName = "distribution"; - iniFile.append("distribution.ini"); - if (iniFile.exists()) - this._iniFile = iniFile; -} - -DistributionCustomizer.prototype = { - _iniFile: null, - - get _ini() { - let ini = Cc["@mozilla.org/xpcom/ini-parser-factory;1"]. - getService(Ci.nsIINIParserFactory). - createINIParser(this._iniFile); - this.__defineGetter__("_ini", function() ini); - return this._ini; - }, - - get _locale() { - let locale = this._prefs.getCharPref("general.useragent.locale", "en-US"); - this.__defineGetter__("_locale", function() locale); - return this._locale; - }, - - get _prefSvc() { - let svc = Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefService); - this.__defineGetter__("_prefSvc", function() svc); - return this._prefSvc; - }, - - get _prefs() { - let branch = this._prefSvc.getBranch(null); - this.__defineGetter__("_prefs", function() branch); - return this._prefs; - }, - - get _ioSvc() { - let svc = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - this.__defineGetter__("_ioSvc", function() svc); - return this._ioSvc; - }, - - _makeURI: function DIST__makeURI(spec) { - return this._ioSvc.newURI(spec, null, null); - }, - - _parseBookmarksSection: - function DIST_parseBookmarksSection(parentId, section) { - let keys = []; - for (let i in enumerate(this._ini.getKeys(section))) - keys.push(i); - keys.sort(); - - let items = {}; - let defaultItemId = -1; - let maxItemId = -1; - - for (let i = 0; i < keys.length; i++) { - let m = /^item\.(\d+)\.(\w+)\.?(\w*)/.exec(keys[i]); - if (m) { - let [foo, iid, iprop, ilocale] = m; - iid = parseInt(iid); - - if (ilocale) - continue; - - if (!items[iid]) - items[iid] = {}; - if (keys.indexOf(keys[i] + "." + this._locale) >= 0) { - items[iid][iprop] = this._ini.getString(section, keys[i] + "." + - this._locale); - } else { - items[iid][iprop] = this._ini.getString(section, keys[i]); - } - - if (iprop == "type" && items[iid]["type"] == "default") - defaultItemId = iid; - - if (maxItemId < iid) - maxItemId = iid; - } else { - dump("Key did not match: " + keys[i] + "\n"); - } - } - - let prependIndex = 0; - for (let iid = 0; iid <= maxItemId; iid++) { - if (!items[iid]) - continue; - - let index = PlacesUtils.bookmarks.DEFAULT_INDEX; - let newId; - - switch (items[iid]["type"]) { - case "default": - break; - - case "folder": - if (iid < defaultItemId) - index = prependIndex++; - - newId = PlacesUtils.bookmarks.createFolder(parentId, - items[iid]["title"], - index); - - this._parseBookmarksSection(newId, "BookmarksFolder-" + - items[iid]["folderId"]); - - if (items[iid]["description"]) - PlacesUtils.annotations.setItemAnnotation(newId, - "bookmarkProperties/description", - items[iid]["description"], 0, - PlacesUtils.annotations.EXPIRE_NEVER); - - break; - - case "separator": - if (iid < defaultItemId) - index = prependIndex++; - PlacesUtils.bookmarks.insertSeparator(parentId, index); - break; - - case "livemark": - if (iid < defaultItemId) - index = prependIndex++; - - // Don't bother updating the livemark contents on creation. - PlacesUtils.livemarks.addLivemark({ title: items[iid]["title"] - , parentId: parentId - , index: index - , feedURI: this._makeURI(items[iid]["feedLink"]) - , siteURI: this._makeURI(items[iid]["siteLink"]) - }).then(null, Cu.reportError); - break; - - case "bookmark": - default: - if (iid < defaultItemId) - index = prependIndex++; - - newId = PlacesUtils.bookmarks.insertBookmark(parentId, - this._makeURI(items[iid]["link"]), - index, items[iid]["title"]); - - if (items[iid]["description"]) - PlacesUtils.annotations.setItemAnnotation(newId, - "bookmarkProperties/description", - items[iid]["description"], 0, - PlacesUtils.annotations.EXPIRE_NEVER); - - break; - } - } - }, - - _customizationsApplied: false, - applyCustomizations: function DIST_applyCustomizations() { - this._customizationsApplied = true; - if (!this._iniFile) - return this._checkCustomizationComplete(); - - // nsPrefService loads very early. Reload prefs so we can set - // distribution defaults during the prefservice:after-app-defaults - // notification (see applyPrefDefaults below) - this._prefSvc.QueryInterface(Ci.nsIObserver); - this._prefSvc.observe(null, "reload-default-prefs", null); - }, - - _bookmarksApplied: false, - applyBookmarks: function DIST_applyBookmarks() { - this._bookmarksApplied = true; - if (!this._iniFile) - return this._checkCustomizationComplete(); - - let sections = enumToObject(this._ini.getSections()); - - // The global section, and several of its fields, is required - // (we also check here to be consistent with applyPrefDefaults below) - if (!sections["Global"]) - return this._checkCustomizationComplete(); - let globalPrefs = enumToObject(this._ini.getKeys("Global")); - if (!(globalPrefs["id"] && globalPrefs["version"] && globalPrefs["about"])) - return this._checkCustomizationComplete(); - - let bmProcessedPref; - try { - bmProcessedPref = this._ini.getString("Global", - "bookmarks.initialized.pref"); - } - catch (e) { - bmProcessedPref = "distribution." + - this._ini.getString("Global", "id") + ".bookmarksProcessed"; - } - - let bmProcessed = this._prefs.getBoolPref(bmProcessedPref, false); - - if (!bmProcessed) { - if (sections["BookmarksMenu"]) - this._parseBookmarksSection(PlacesUtils.bookmarksMenuFolderId, - "BookmarksMenu"); - if (sections["BookmarksToolbar"]) - this._parseBookmarksSection(PlacesUtils.toolbarFolderId, - "BookmarksToolbar"); - this._prefs.setBoolPref(bmProcessedPref, true); - } - return this._checkCustomizationComplete(); - }, - - _prefDefaultsApplied: false, - applyPrefDefaults: function DIST_applyPrefDefaults() { - this._prefDefaultsApplied = true; - if (!this._iniFile) - return this._checkCustomizationComplete(); - - let sections = enumToObject(this._ini.getSections()); - - // The global section, and several of its fields, is required - if (!sections["Global"]) - return this._checkCustomizationComplete(); - let globalPrefs = enumToObject(this._ini.getKeys("Global")); - if (!(globalPrefs["id"] && globalPrefs["version"] && globalPrefs["about"])) - return this._checkCustomizationComplete(); - - let defaults = this._prefSvc.getDefaultBranch(null); - - // Global really contains info we set as prefs. They're only - // separate because they are "special" (read: required) - - defaults.setCharPref("distribution.id", this._ini.getString("Global", "id")); - defaults.setCharPref("distribution.version", - this._ini.getString("Global", "version")); - - let partnerAbout = Cc["@mozilla.org/supports-string;1"]. - createInstance(Ci.nsISupportsString); - try { - if (globalPrefs["about." + this._locale]) { - partnerAbout.data = this._ini.getString("Global", "about." + this._locale); - } else { - partnerAbout.data = this._ini.getString("Global", "about"); - } - defaults.setComplexValue("distribution.about", - Ci.nsISupportsString, partnerAbout); - } catch (e) { - /* ignore bad prefs due to bug 895473 and move on */ - Cu.reportError(e); - } - - if (sections["Preferences"]) { - for (let key in enumerate(this._ini.getKeys("Preferences"))) { - try { - let value = eval(this._ini.getString("Preferences", key)); - switch (typeof value) { - case "boolean": - defaults.setBoolPref(key, value); - break; - case "number": - defaults.setIntPref(key, value); - break; - case "string": - defaults.setCharPref(key, value); - break; - case "undefined": - defaults.setCharPref(key, value); - break; - } - } catch (e) { /* ignore bad prefs and move on */ } - } - } - - // We eval() the localizable prefs as well (even though they'll - // always get set as a string) to keep the INI format consistent: - // string prefs always need to be in quotes - - let localizedStr = Cc["@mozilla.org/pref-localizedstring;1"]. - createInstance(Ci.nsIPrefLocalizedString); - - if (sections["LocalizablePreferences"]) { - for (let key in enumerate(this._ini.getKeys("LocalizablePreferences"))) { - try { - let value = eval(this._ini.getString("LocalizablePreferences", key)); - value = value.replace("%LOCALE%", this._locale, "g"); - localizedStr.data = "data:text/plain," + key + "=" + value; - defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr); - } catch (e) { /* ignore bad prefs and move on */ } - } - } - - if (sections["LocalizablePreferences-" + this._locale]) { - for (let key in enumerate(this._ini.getKeys("LocalizablePreferences-" + this._locale))) { - try { - let value = eval(this._ini.getString("LocalizablePreferences-" + this._locale, key)); - localizedStr.data = "data:text/plain," + key + "=" + value; - defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr); - } catch (e) { /* ignore bad prefs and move on */ } - } - } - - return this._checkCustomizationComplete(); - }, - - _checkCustomizationComplete: function DIST__checkCustomizationComplete() { - let prefDefaultsApplied = this._prefDefaultsApplied || !this._iniFile; - if (this._customizationsApplied && this._bookmarksApplied && - prefDefaultsApplied) { - let os = Cc["@mozilla.org/observer-service;1"]. - getService(Ci.nsIObserverService); - os.notifyObservers(null, DISTRIBUTION_CUSTOMIZATION_COMPLETE_TOPIC, null); - } - } -}; - -function enumerate(UTF8Enumerator) { - while (UTF8Enumerator.hasMore()) - yield UTF8Enumerator.getNext(); -} - -function enumToObject(UTF8Enumerator) { - let ret = {}; - for (let i in enumerate(UTF8Enumerator)) - ret[i] = 1; - return ret; -} diff --git a/components/downloads/BrowserDownloads.manifest b/components/downloads/BrowserDownloads.manifest deleted file mode 100644 index 1881ca1..0000000 --- a/components/downloads/BrowserDownloads.manifest +++ /dev/null @@ -1,4 +0,0 @@ -component {49507fe5-2cee-4824-b6a3-e999150ce9b8} DownloadsStartup.js -contract @mozilla.org/browser/downloadsstartup;1 {49507fe5-2cee-4824-b6a3-e999150ce9b8} -category profile-after-change DownloadsStartup @mozilla.org/browser/downloadsstartup;1 -component {4d99321e-d156-455b-81f7-e7aa2308134f} DownloadsUI.js diff --git a/components/downloads/DownloadsCommon.jsm b/components/downloads/DownloadsCommon.jsm deleted file mode 100644 index efe31ce..0000000 --- a/components/downloads/DownloadsCommon.jsm +++ /dev/null @@ -1,1920 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -this.EXPORTED_SYMBOLS = [ - "DownloadsCommon", -]; - -/** - * Handles the Downloads panel shared methods and data access. - * - * This file includes the following constructors and global objects: - * - * DownloadsCommon - * This object is exposed directly to the consumers of this JavaScript module, - * and provides shared methods for all the instances of the user interface. - * - * DownloadsData - * Retrieves the list of past and completed downloads from the underlying - * Downloads API data, and provides asynchronous notifications allowing - * to build a consistent view of the available data. - * - * DownloadsIndicatorData - * This object registers itself with DownloadsData as a view, and transforms the - * notifications it receives into overall status data, that is then broadcast to - * the registered download status indicators. - */ - -//////////////////////////////////////////////////////////////////////////////// -//// Globals - -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; -const Cr = Components.results; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", - "resource://gre/modules/NetUtil.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PluralForm", - "resource://gre/modules/PluralForm.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Downloads", - "resource://gre/modules/Downloads.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "DownloadUIHelper", - "resource://gre/modules/DownloadUIHelper.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils", - "resource://gre/modules/DownloadUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", - "resource://gre/modules/FileUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "OS", - "resource://gre/modules/osfile.jsm") -XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", - "resource://gre/modules/PlacesUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", - "resource://gre/modules/PrivateBrowsingUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow", - "resource:///modules/RecentWindow.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", - "resource://gre/modules/PlacesUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Promise", - "resource://gre/modules/Promise.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "DownloadsLogger", - "resource:///modules/DownloadsLogger.jsm"); - -const nsIDM = Ci.nsIDownloadManager; - -const kDownloadsStringBundleUrl = - "chrome://browser/locale/downloads/downloads.properties"; - -const kPrefConfirmOpenExe = "browser.download.confirmOpenExecutable"; - -const kDownloadsStringsRequiringFormatting = { - sizeWithUnits: true, - shortTimeLeftSeconds: true, - shortTimeLeftMinutes: true, - shortTimeLeftHours: true, - shortTimeLeftDays: true, - statusSeparator: true, - statusSeparatorBeforeNumber: true, - fileExecutableSecurityWarning: true -}; - -const kDownloadsStringsRequiringPluralForm = { - otherDownloads2: true -}; - -const kPartialDownloadSuffix = ".part"; - -const kPrefBranch = Services.prefs.getBranch("browser.download."); - -var PrefObserver = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, - Ci.nsISupportsWeakReference]), - getPref: function PO_getPref(name) { - try { - switch (typeof this.prefs[name]) { - case "boolean": - return kPrefBranch.getBoolPref(name); - } - } catch (ex) { } - return this.prefs[name]; - }, - observe: function PO_observe(aSubject, aTopic, aData) { - if (this.prefs.hasOwnProperty(aData)) { - return this[aData] = this.getPref(aData); - } - }, - register: function PO_register(prefs) { - this.prefs = prefs; - kPrefBranch.addObserver("", this, true); - for (let key in prefs) { - let name = key; - XPCOMUtils.defineLazyGetter(this, name, function () { - return PrefObserver.getPref(name); - }); - } - }, -}; - -PrefObserver.register({ - // prefName: defaultValue - debug: false, - animateNotifications: true -}); - - -//////////////////////////////////////////////////////////////////////////////// -//// DownloadsCommon - -/** - * This object is exposed directly to the consumers of this JavaScript module, - * and provides shared methods for all the instances of the user interface. - */ -this.DownloadsCommon = { - log: function DC_log(...aMessageArgs) { - delete this.log; - this.log = function DC_log(...aMessageArgs) { - if (!PrefObserver.debug) { - return; - } - DownloadsLogger.log.apply(DownloadsLogger, aMessageArgs); - } - this.log.apply(this, aMessageArgs); - }, - - error: function DC_error(...aMessageArgs) { - delete this.error; - this.error = function DC_error(...aMessageArgs) { - if (!PrefObserver.debug) { - return; - } - DownloadsLogger.reportError.apply(DownloadsLogger, aMessageArgs); - } - this.error.apply(this, aMessageArgs); - }, - /** - * Returns an object whose keys are the string names from the downloads string - * bundle, and whose values are either the translated strings or functions - * returning formatted strings. - */ - get strings() - { - let strings = {}; - let sb = Services.strings.createBundle(kDownloadsStringBundleUrl); - let enumerator = sb.getSimpleEnumeration(); - while (enumerator.hasMoreElements()) { - let string = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement); - let stringName = string.key; - if (stringName in kDownloadsStringsRequiringFormatting) { - strings[stringName] = function () { - // Convert "arguments" to a real array before calling into XPCOM. - return sb.formatStringFromName(stringName, - Array.slice(arguments, 0), - arguments.length); - }; - } else if (stringName in kDownloadsStringsRequiringPluralForm) { - strings[stringName] = function (aCount) { - // Convert "arguments" to a real array before calling into XPCOM. - let formattedString = sb.formatStringFromName(stringName, - Array.slice(arguments, 0), - arguments.length); - return PluralForm.get(aCount, formattedString); - }; - } else { - strings[stringName] = string.value; - } - } - delete this.strings; - return this.strings = strings; - }, - - /** - * Generates a very short string representing the given time left. - * - * @param aSeconds - * Value to be formatted. It represents the number of seconds, it must - * be positive but does not need to be an integer. - * - * @return Formatted string, for example "30s" or "2h". The returned value is - * maximum three characters long, at least in English. - */ - formatTimeLeft: function DC_formatTimeLeft(aSeconds) - { - // Decide what text to show for the time - let seconds = Math.round(aSeconds); - if (!seconds) { - return ""; - } else if (seconds <= 30) { - return DownloadsCommon.strings["shortTimeLeftSeconds"](seconds); - } - let minutes = Math.round(aSeconds / 60); - if (minutes < 60) { - return DownloadsCommon.strings["shortTimeLeftMinutes"](minutes); - } - let hours = Math.round(minutes / 60); - if (hours < 48) { // two days - return DownloadsCommon.strings["shortTimeLeftHours"](hours); - } - let days = Math.round(hours / 24); - return DownloadsCommon.strings["shortTimeLeftDays"](Math.min(days, 99)); - }, - - /** - * Indicates whether we should show the full Download Manager window interface - * instead of the simplified panel interface. The behavior of downloads - * across browsing session is consistent with the selected interface. - */ - get useToolkitUI() - { - /* Toolkit UI is currently incompatible. - * FIXME: Either fix the toolkitUI (make DBConnection work) or remove - * the unused code altogether - */ - //try { - // return Services.prefs.getBoolPref("browser.download.useToolkitUI"); - //} catch (ex) { } - return false; - }, - - /** - * Indicates whether we should show visual notification on the indicator - * when a download event is triggered. - */ - get animateNotifications() - { - return PrefObserver.animateNotifications; - }, - - /** - * Get access to one of the DownloadsData or PrivateDownloadsData objects, - * depending on the privacy status of the window in question. - * - * @param aWindow - * The browser window which owns the download button. - */ - getData: function DC_getData(aWindow) { - if (PrivateBrowsingUtils.isWindowPrivate(aWindow)) { - return PrivateDownloadsData; - } else { - return DownloadsData; - } - }, - - /** - * Initializes the data link for both the private and non-private downloads - * data objects. - * - * @param aDownloadManagerService - * Reference to the service implementing nsIDownloadManager. We need - * this because getService isn't available for us when this method is - * called, and we must ensure to register our listeners before the - * getService call for the Download Manager returns. - */ - initializeAllDataLinks: function DC_initializeAllDataLinks(aDownloadManagerService) { - DownloadsData.initializeDataLink(aDownloadManagerService); - PrivateDownloadsData.initializeDataLink(aDownloadManagerService); - }, - - /** - * Terminates the data link for both the private and non-private downloads - * data objects. - */ - terminateAllDataLinks: function DC_terminateAllDataLinks() { - DownloadsData.terminateDataLink(); - PrivateDownloadsData.terminateDataLink(); - }, - - /** - * Reloads the specified kind of downloads from the non-private store. - * This method must only be called when Private Browsing Mode is disabled. - * - * @param aActiveOnly - * True to load only active downloads from the database. - */ - ensureAllPersistentDataLoaded: - function DC_ensureAllPersistentDataLoaded(aActiveOnly) { - DownloadsData.ensurePersistentDataLoaded(aActiveOnly); - }, - - /** - * Get access to one of the DownloadsIndicatorData or - * PrivateDownloadsIndicatorData objects, depending on the privacy status of - * the window in question. - */ - getIndicatorData: function DC_getIndicatorData(aWindow) { - if (PrivateBrowsingUtils.isWindowPrivate(aWindow)) { - return PrivateDownloadsIndicatorData; - } else { - return DownloadsIndicatorData; - } - }, - - /** - * Returns a reference to the DownloadsSummaryData singleton - creating one - * in the process if one hasn't been instantiated yet. - * - * @param aWindow - * The browser window which owns the download button. - * @param aNumToExclude - * The number of items on the top of the downloads list to exclude - * from the summary. - */ - getSummary: function DC_getSummary(aWindow, aNumToExclude) - { - if (PrivateBrowsingUtils.isWindowPrivate(aWindow)) { - if (this._privateSummary) { - return this._privateSummary; - } - return this._privateSummary = new DownloadsSummaryData(true, aNumToExclude); - } else { - if (this._summary) { - return this._summary; - } - return this._summary = new DownloadsSummaryData(false, aNumToExclude); - } - }, - _summary: null, - _privateSummary: null, - - /** - * Returns the legacy state integer value for the provided Download object. - */ - stateOfDownload(download) { - // Collapse state using the correct priority. - if (!download.stopped) { - return nsIDM.DOWNLOAD_DOWNLOADING; - } - if (download.succeeded) { - return nsIDM.DOWNLOAD_FINISHED; - } - if (download.error) { - if (download.error.becauseBlockedByParentalControls) { - return nsIDM.DOWNLOAD_BLOCKED_PARENTAL; - } - if (download.error.becauseBlockedByReputationCheck) { - return nsIDM.DOWNLOAD_DIRTY; - } - return nsIDM.DOWNLOAD_FAILED; - } - if (download.canceled) { - if (download.hasPartialData) { - return nsIDM.DOWNLOAD_PAUSED; - } - return nsIDM.DOWNLOAD_CANCELED; - } - return nsIDM.DOWNLOAD_NOTSTARTED; - }, - - /** - * Helper function required because the Downloads Panel and the Downloads View - * don't share the controller yet. - */ - removeAndFinalizeDownload(download) { - Downloads.getList(Downloads.ALL) - .then(list => list.remove(download)) - .then(() => download.finalize(true)) - .catch(Cu.reportError); - }, - - /** - * Given an iterable collection of Download objects, generates and returns - * statistics about that collection. - * - * @param downloads An iterable collection of Download objects. - * - * @return Object whose properties are the generated statistics. Currently, - * we return the following properties: - * - * numActive : The total number of downloads. - * numPaused : The total number of paused downloads. - * numDownloading : The total number of downloads being downloaded. - * totalSize : The total size of all downloads once completed. - * totalTransferred: The total amount of transferred data for these - * downloads. - * slowestSpeed : The slowest download rate. - * rawTimeLeft : The estimated time left for the downloads to - * complete. - * percentComplete : The percentage of bytes successfully downloaded. - */ - summarizeDownloads(downloads) { - let summary = { - numActive: 0, - numPaused: 0, - numDownloading: 0, - totalSize: 0, - totalTransferred: 0, - // slowestSpeed is Infinity so that we can use Math.min to - // find the slowest speed. We'll set this to 0 afterwards if - // it's still at Infinity by the time we're done iterating all - // download. - slowestSpeed: Infinity, - rawTimeLeft: -1, - percentComplete: -1 - } - - for (let download of downloads) { - summary.numActive++; - - if (!download.stopped) { - summary.numDownloading++; - if (download.hasProgress && download.speed > 0) { - let sizeLeft = download.totalBytes - download.currentBytes; - summary.rawTimeLeft = Math.max(summary.rawTimeLeft, - sizeLeft / download.speed); - summary.slowestSpeed = Math.min(summary.slowestSpeed, - download.speed); - } - } else if (download.canceled && download.hasPartialData) { - summary.numPaused++; - } - // Only add to total values if we actually know the download size. - if (download.succeeded) { - summary.totalSize += download.target.size; - summary.totalTransferred += download.target.size; - } else if (download.hasProgress) { - summary.totalSize += download.totalBytes; - summary.totalTransferred += download.currentBytes; - } - } - - if (summary.totalSize != 0) { - summary.percentComplete = (summary.totalTransferred / - summary.totalSize) * 100; - } - - if (summary.slowestSpeed == Infinity) { - summary.slowestSpeed = 0; - } - - return summary; - }, - - /** - * If necessary, smooths the estimated number of seconds remaining for one - * or more downloads to complete. - * - * @param aSeconds - * Current raw estimate on number of seconds left for one or more - * downloads. This is a floating point value to help get sub-second - * accuracy for current and future estimates. - */ - smoothSeconds: function DC_smoothSeconds(aSeconds, aLastSeconds) - { - // We apply an algorithm similar to the DownloadUtils.getTimeLeft function, - // though tailored to a single time estimation for all downloads. We never - // apply something if the new value is less than half the previous value. - let shouldApplySmoothing = aLastSeconds >= 0 && - aSeconds > aLastSeconds / 2; - if (shouldApplySmoothing) { - // Apply hysteresis to favor downward over upward swings. Trust only 30% - // of the new value if lower, and 10% if higher (exponential smoothing). - let diff = aSeconds - aLastSeconds; - aSeconds = aLastSeconds + (diff < 0 ? .3 : .1) * diff; - - // If the new time is similar, reuse something close to the last time - // left, but subtract a little to provide forward progress. - diff = aSeconds - aLastSeconds; - let diffPercent = diff / aLastSeconds * 100; - if (Math.abs(diff) < 5 || Math.abs(diffPercent) < 5) { - aSeconds = aLastSeconds - (diff < 0 ? .4 : .2); - } - } - - // In the last few seconds of downloading, we are always subtracting and - // never adding to the time left. Ensure that we never fall below one - // second left until all downloads are actually finished. - return aLastSeconds = Math.max(aSeconds, 1); - }, - - /** - * Opens a downloaded file. - * - * @param aFile - * the downloaded file to be opened. - * @param aMimeInfo - * the mime type info object. May be null. - * @param aOwnerWindow - * the window with which this action is associated. - */ - openDownloadedFile: function DC_openDownloadedFile(aFile, aMimeInfo, aOwnerWindow) { - if (!(aFile instanceof Ci.nsIFile)) - throw new Error("aFile must be a nsIFile object"); - if (aMimeInfo && !(aMimeInfo instanceof Ci.nsIMIMEInfo)) - throw new Error("Invalid value passed for aMimeInfo"); - if (!(aOwnerWindow instanceof Ci.nsIDOMWindow)) - throw new Error("aOwnerWindow must be a dom-window object"); - -#ifdef XP_WIN - // On Windows, the system will provide a native confirmation prompt - // for .exe files. Exclude this from our prompt, but prompt on other - // executable types. - let isWindowsExe = aFile.leafName.toLowerCase().endsWith(".exe"); -#else - let isWindowsExe = false; -#endif - - // Confirm opening executable files if required. - if (aFile.isExecutable() && !isWindowsExe) { - let showAlert = true; - try { - showAlert = Services.prefs.getBoolPref(kPrefConfirmOpenExe); - } catch (ex) { - // If the preference does not exist, continue with the prompt. - } - - if (showAlert) { - let name = aFile.leafName; - let message = - DownloadsCommon.strings.fileExecutableSecurityWarning(name, name); - let title = - DownloadsCommon.strings.fileExecutableSecurityWarningTitle; - - let open = Services.prompt.confirm(aOwnerWindow, title, message); - if (!open) { - return; - } - } - } - - // Actually open the file. - try { - if (aMimeInfo && aMimeInfo.preferredAction == aMimeInfo.useHelperApp) { - aMimeInfo.launchWithFile(aFile); - return; - } - } - catch(ex) { } - - // If either we don't have the mime info, or the preferred action failed, - // attempt to launch the file directly. - try { - aFile.launch(); - } - catch(ex) { - // If launch fails, try sending it through the system's external "file:" - // URL handler. - Cc["@mozilla.org/uriloader/external-protocol-service;1"] - .getService(Ci.nsIExternalProtocolService) - .loadUrl(NetUtil.newURI(aFile)); - } - }, - - /** - * Show a downloaded file in the system file manager. - * - * @param aFile - * a downloaded file. - */ - showDownloadedFile: function DC_showDownloadedFile(aFile) { - if (!(aFile instanceof Ci.nsIFile)) - throw new Error("aFile must be a nsIFile object"); - try { - // Show the directory containing the file and select the file. - aFile.reveal(); - } catch (ex) { - // If reveal fails for some reason (e.g., it's not implemented on unix - // or the file doesn't exist), try using the parent if we have it. - let parent = aFile.parent; - if (parent) { - try { - // Open the parent directory to show where the file should be. - parent.launch(); - } catch (ex) { - // If launch also fails (probably because it's not implemented), let - // the OS handler try to open the parent. - Cc["@mozilla.org/uriloader/external-protocol-service;1"] - .getService(Ci.nsIExternalProtocolService) - .loadUrl(NetUtil.newURI(parent)); - } - } - } - } -}; - -/** - * Returns true if we are executing on Windows Vista or a later version. - */ -XPCOMUtils.defineLazyGetter(DownloadsCommon, "isWinVistaOrHigher", function () { - let os = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS; - if (os != "WINNT") { - return false; - } - let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2); - return parseFloat(sysInfo.getProperty("version")) >= 6; -}); - -/** - * Returns true to indicate that we should hook the panel to the JavaScript API - * for downloads instead of the nsIDownloadManager back-end. - * This is kept for compatibility/leftovers and should be removed later. - */ -XPCOMUtils.defineLazyGetter(DownloadsCommon, "useJSTransfer", function () { - return true; -}); - -//////////////////////////////////////////////////////////////////////////////// -//// DownloadsData - -/** - * Retrieves the list of past and completed downloads from the underlying - * Download Manager data, and provides asynchronous notifications allowing to - * build a consistent view of the available data. - * - * This object responds to real-time changes in the underlying Download Manager - * data. For example, the deletion of one or more downloads is notified through - * the nsIObserver interface, while any state or progress change is notified - * through the nsIDownloadProgressListener interface. - * - * Note that using this object does not automatically start the Download Manager - * service. Consumers will see an empty list of downloads until the service is - * actually started. This is useful to display a neutral progress indicator in - * the main browser window until the autostart timeout elapses. - * - * Note that DownloadsData and PrivateDownloadsData are two equivalent singleton - * objects, one accessing non-private downloads, and the other accessing private - * ones. - */ -function DownloadsDataCtor(aPrivate) { - this._isPrivate = aPrivate; - - // Contains all the available Download objects and their integer state. - this.oldDownloadStates = new Map(); - - // Array of view objects that should be notified when the available download - // data changes. - this._views = []; -} - -DownloadsDataCtor.prototype = { - /** - * Starts receiving events for current downloads. - */ - initializeDataLink() { - if (!this._dataLinkInitialized) { - let promiseList = Downloads.getList(this._isPrivate ? Downloads.PRIVATE - : Downloads.PUBLIC); - promiseList.then(list => list.addView(this)).then(null, Cu.reportError); - this._dataLinkInitialized = true; - } - }, - _dataLinkInitialized: false, - - /** - * Stops receiving events for current downloads and cancels any pending read. - */ - terminateDataLink: function DD_terminateDataLink() - { - Cu.reportError("terminateDataLink not applicable with JS Transfers"); - return; - }, - - /** - * Iterator for all the available Download objects. This is empty until the - * data has been loaded using the JavaScript API for downloads. - */ - get downloads() this.oldDownloadStates.keys(), - - /** - * True if there are finished downloads that can be removed from the list. - */ - get canRemoveFinished() - { - for (let download of this.downloads) { - // Stopped, paused, and failed downloads with partial data are removed. - if (download.stopped && !(download.canceled && download.hasPartialData)) { - return true; - } - } - return false; - }, - - /** - * Asks the back-end to remove finished downloads from the list. - */ - removeFinished: function DD_removeFinished() - { - let promiseList = Downloads.getList(this._isPrivate ? Downloads.PRIVATE - : Downloads.PUBLIC); - promiseList.then(list => list.removeFinished()) - .then(null, Cu.reportError); - }, - - ////////////////////////////////////////////////////////////////////////////// - //// Integration with the asynchronous Downloads back-end - - onDownloadAdded(download) { - // Download objects do not store the end time of downloads, as the Downloads - // API does not need to persist this information for all platforms. Once a - // download terminates on a Desktop browser, it becomes a history download, - // for which the end time is stored differently, as a Places annotation. - download.endTime = Date.now(); - - this.oldDownloadStates.set(download, - DownloadsCommon.stateOfDownload(download)); - - for (let view of this._views) { - view.onDownloadAdded(download, true); - } - }, - - onDownloadChanged(download) { - let oldState = this.oldDownloadStates.get(download); - let newState = DownloadsCommon.stateOfDownload(download); - this.oldDownloadStates.set(download, newState); - - if (oldState != newState) { - if (download.succeeded || - (download.canceled && !download.hasPartialData) || - download.error) { - // Store the end time that may be displayed by the views. - download.endTime = Date.now(); - - // This state transition code should actually be located in a Downloads - // API module (bug 941009). Moreover, the fact that state is stored as - // annotations should be ideally hidden behind methods of - // nsIDownloadHistory (bug 830415). - if (!this._isPrivate) { - try { - let downloadMetaData = { - state: DownloadsCommon.stateOfDownload(download), - endTime: download.endTime, - }; - if (download.succeeded) { - downloadMetaData.fileSize = download.target.size; - } - - PlacesUtils.annotations.setPageAnnotation( - NetUtil.newURI(download.source.url), - "downloads/metaData", - JSON.stringify(downloadMetaData), 0, - PlacesUtils.annotations.EXPIRE_WITH_HISTORY); - } catch (ex) { - Cu.reportError(ex); - } - } - } - - for (let view of this._views) { - try { - view.onDownloadStateChanged(download); - } catch (ex) { - Cu.reportError(ex); - } - } - - if (download.succeeded || - (download.error && download.error.becauseBlocked)) { - this._notifyDownloadEvent("finish"); - } - } - - if (!download.newDownloadNotified) { - download.newDownloadNotified = true; - this._notifyDownloadEvent("start"); - } - - for (let view of this._views) { - view.onDownloadChanged(download); - } - }, - - onDownloadRemoved(download) { - this.oldDownloadStates.delete(download); - - for (let view of this._views) { - view.onDownloadRemoved(download); - } - }, - - ////////////////////////////////////////////////////////////////////////////// - //// Registration of views - - /** - * Adds an object to be notified when the available download data changes. - * The specified object is initialized with the currently available downloads. - * - * @param aView - * DownloadsView object to be added. This reference must be passed to - * removeView before termination. - */ - addView: function DD_addView(aView) - { - this._views.push(aView); - this._updateView(aView); - }, - - /** - * Removes an object previously added using addView. - * - * @param aView - * DownloadsView object to be removed. - */ - removeView: function DD_removeView(aView) - { - let index = this._views.indexOf(aView); - if (index != -1) { - this._views.splice(index, 1); - } - }, - - /** - * Ensures that the currently loaded data is added to the specified view. - * - * @param aView - * DownloadsView object to be initialized. - */ - _updateView: function DD_updateView(aView) - { - // Indicate to the view that a batch loading operation is in progress. - aView.onDataLoadStarting(); - - // Sort backwards by start time, ensuring that the most recent - // downloads are added first regardless of their state. - // Tycho: - //let loadedItemsArray = [dataItem - // for each (dataItem in this.dataItems) - // if (dataItem)]; - let downloadsArray = [...this.downloads]; - downloadsArray.sort((a, b) => b.startTime - a.startTime); - downloadsArray.forEach(download => aView.onDownloadAdded(download, false)); - - // Notify the view that all data is available unless loading is in progress. - if (!this._pendingStatement) { - aView.onDataLoadCompleted(); - } - }, - - ////////////////////////////////////////////////////////////////////////////// - //// In-memory downloads data store - - /** - * Clears the loaded data. - */ - clear: function DD_clear() - { - this._terminateDataAccess(); - this.dataItems = {}; - }, - - /** - * Returns the data item associated with the provided source object. The - * source can be a download object that we received from the Download Manager - * because of a real-time notification, or a row from the downloads database, - * during the asynchronous data load. - * - * In case we receive download status notifications while we are still - * populating the list of downloads from the database, we want the real-time - * status to take precedence over the state that is read from the database, - * which might be older. This is achieved by creating the download item if - * it's not already in the list, but never updating the returned object using - * the data from the database, if the object already exists. - * - * @param aSource - * Object containing the data with which the item should be initialized - * if it doesn't already exist in the list. This should implement - * either nsIDownload or mozIStorageRow. If the item exists, this - * argument is only used to retrieve the download identifier. - * @param aMayReuseGUID - * If false, indicates that the download should not be added if a - * download with the same identifier was removed in the meantime. This - * ensures that, while loading the list asynchronously, downloads that - * have been removed in the meantime do no reappear inadvertently. - * - * @return New or existing data item, or null if the item was deleted from the - * list of available downloads. - */ - _getOrAddDataItem: function DD_getOrAddDataItem(aSource, aMayReuseGUID) - { - let downloadGuid = (aSource instanceof Ci.nsIDownload) - ? aSource.guid - : aSource.getResultByName("guid"); - if (downloadGuid in this.dataItems) { - let existingItem = this.dataItems[downloadGuid]; - if (existingItem || !aMayReuseGUID) { - // Returns null if the download was removed and we can't reuse the item. - return existingItem; - } - } - DownloadsCommon.log("Creating a new DownloadsDataItem with downloadGuid =", - downloadGuid); - let dataItem = new DownloadsDataItem(aSource); - this.dataItems[downloadGuid] = dataItem; - - // Create the view items before returning. - let addToStartOfList = aSource instanceof Ci.nsIDownload; - this._views.forEach( - function (view) view.onDataItemAdded(dataItem, addToStartOfList) - ); - return dataItem; - }, - - /** - * Removes the data item with the specified identifier. - * - * This method can be called at most once per download identifier. - */ - _removeDataItem: function DD_removeDataItem(aDownloadId) - { - if (aDownloadId in this.dataItems) { - let dataItem = this.dataItems[aDownloadId]; - this.dataItems[aDownloadId] = null; - this._views.forEach( - function (view) view.onDataItemRemoved(dataItem) - ); - } - }, - - ////////////////////////////////////////////////////////////////////////////// - //// Persistent data loading - - /** - * Represents an executing statement, allowing its cancellation. - */ - _pendingStatement: null, - - /** - * Indicates which kind of items from the persistent downloads database have - * been fully loaded in memory and are available to the views. This can - * assume the value of one of the kLoad constants. - */ - _loadState: 0, - - /** No downloads have been fully loaded yet. */ - get kLoadNone() 0, - /** All the active downloads in the database are loaded in memory. */ - get kLoadActive() 1, - /** All the downloads in the database are loaded in memory. */ - get kLoadAll() 2, - - /** - * Reloads the specified kind of downloads from the persistent database. This - * method must only be called when Private Browsing Mode is disabled. - * - * @param aActiveOnly - * True to load only active downloads from the database. - */ - ensurePersistentDataLoaded: - function DD_ensurePersistentDataLoaded(aActiveOnly) - { - if (this == PrivateDownloadsData) { - Cu.reportError("ensurePersistentDataLoaded should not be called on PrivateDownloadsData"); - return; - } - - if (this._pendingStatement) { - // We are already in the process of reloading all downloads. - return; - } - - if (aActiveOnly) { - if (this._loadState == this.kLoadNone) { - DownloadsCommon.log("Loading only active downloads from the persistence database"); - // Indicate to the views that a batch loading operation is in progress. - this._views.forEach( - function (view) view.onDataLoadStarting() - ); - - // Reload the list using the Download Manager service. The list is - // returned in no particular order. - let downloads = Services.downloads.activeDownloads; - while (downloads.hasMoreElements()) { - let download = downloads.getNext().QueryInterface(Ci.nsIDownload); - this._getOrAddDataItem(download, true); - } - this._loadState = this.kLoadActive; - - // Indicate to the views that the batch loading operation is complete. - this._views.forEach( - function (view) view.onDataLoadCompleted() - ); - DownloadsCommon.log("Active downloads done loading."); - } - } else { - if (this._loadState != this.kLoadAll) { - // Load only the relevant columns from the downloads database. The - // columns are read in the _initFromDataRow method of DownloadsDataItem. - // Order by descending download identifier so that the most recent - // downloads are notified first to the listening views. - DownloadsCommon.log("Loading all downloads from the persistence database."); - let dbConnection = Services.downloads.DBConnection; - let statement = dbConnection.createAsyncStatement( - "SELECT guid, target, name, source, referrer, state, " - + "startTime, endTime, currBytes, maxBytes " - + "FROM moz_downloads " - + "ORDER BY startTime DESC" - ); - try { - this._pendingStatement = statement.executeAsync(this); - } finally { - statement.finalize(); - } - } - } - }, - - /** - * Cancels any pending data access and ensures views are notified. - */ - _terminateDataAccess: function DD_terminateDataAccess() - { - if (this._pendingStatement) { - this._pendingStatement.cancel(); - this._pendingStatement = null; - } - - // Close all the views on the current data. Create a copy of the array - // because some views might unregister while processing this event. - Array.slice(this._views, 0).forEach( - function (view) view.onDataInvalidated() - ); - }, - - ////////////////////////////////////////////////////////////////////////////// - //// mozIStorageStatementCallback - - handleResult: function DD_handleResult(aResultSet) - { - for (let row = aResultSet.getNextRow(); - row; - row = aResultSet.getNextRow()) { - // Add the download to the list and initialize it with the data we read, - // unless we already received a notification providing more reliable - // information for this download. - this._getOrAddDataItem(row, false); - } - }, - - handleError: function DD_handleError(aError) - { - DownloadsCommon.error("Database statement execution error (", - aError.result, "): ", aError.message); - }, - - handleCompletion: function DD_handleCompletion(aReason) - { - DownloadsCommon.log("Loading all downloads from database completed with reason:", - aReason); - this._pendingStatement = null; - - // To ensure that we don't inadvertently delete more downloads from the - // database than needed on shutdown, we should update the load state only if - // the operation completed successfully. - if (aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED) { - this._loadState = this.kLoadAll; - } - - // Indicate to the views that the batch loading operation is complete, even - // if the lookup failed or was canceled. The only possible glitch happens - // in case the database backend changes while loading data, when the views - // would open and immediately close. This case is rare enough not to need a - // special treatment. - this._views.forEach( - function (view) view.onDataLoadCompleted() - ); - }, - - ////////////////////////////////////////////////////////////////////////////// - //// nsIObserver - - observe: function DD_observe(aSubject, aTopic, aData) - { - switch (aTopic) { - case "download-manager-remove-download-guid": - // If a single download was removed, remove the corresponding data item. - if (aSubject) { - let downloadGuid = aSubject.QueryInterface(Ci.nsISupportsCString).data; - DownloadsCommon.log("A single download with id", - downloadGuid, "was removed."); - this._removeDataItem(downloadGuid); - break; - } - - // Multiple downloads have been removed. Iterate over known downloads - // and remove those that don't exist anymore. - DownloadsCommon.log("Multiple downloads were removed."); - for each (let dataItem in this.dataItems) { - if (dataItem) { - // Bug 449811 - We have to bind to the dataItem because Javascript - // doesn't do fresh let-bindings per loop iteration. - let dataItemBinding = dataItem; - Services.downloads.getDownloadByGUID(dataItemBinding.downloadGuid, - function(aStatus, aResult) { - if (aStatus == Components.results.NS_ERROR_NOT_AVAILABLE) { - DownloadsCommon.log("Removing download with id", - dataItemBinding.downloadGuid); - this._removeDataItem(dataItemBinding.downloadGuid); - } - }.bind(this)); - } - } - break; - } - }, - - ////////////////////////////////////////////////////////////////////////////// - //// nsIDownloadProgressListener - - onDownloadStateChange: function DD_onDownloadStateChange(aOldState, aDownload) - { - if (aDownload.isPrivate != this._isPrivate) { - // Ignore the downloads with a privacy status other than what we are - // tracking. - return; - } - - // When a new download is added, it may have the same identifier of a - // download that we previously deleted during this session, and we also - // want to provide a visible indication that the download started. - let isNew = aOldState == nsIDM.DOWNLOAD_NOTSTARTED || - aOldState == nsIDM.DOWNLOAD_QUEUED; - - let dataItem = this._getOrAddDataItem(aDownload, isNew); - if (!dataItem) { - return; - } - - let wasInProgress = dataItem.inProgress; - - DownloadsCommon.log("A download changed its state to:", aDownload.state); - dataItem.state = aDownload.state; - dataItem.referrer = aDownload.referrer && aDownload.referrer.spec; - dataItem.resumable = aDownload.resumable; - dataItem.startTime = Math.round(aDownload.startTime / 1000); - dataItem.currBytes = aDownload.amountTransferred; - dataItem.maxBytes = aDownload.size; - - if (wasInProgress && !dataItem.inProgress) { - dataItem.endTime = Date.now(); - } - - // When a download is retried, we create a different download object from - // the database with the same ID as before. This means that the nsIDownload - // that the dataItem holds might now need updating. - // - // We only overwrite this in the event that _download exists, because if it - // doesn't, that means that no caller ever tried to get the nsIDownload, - // which means it was never retrieved and doesn't need to be overwritten. - if (dataItem._download) { - dataItem._download = aDownload; - } - - for (let view of this._views) { - try { - view.getViewItem(dataItem).onStateChange(aOldState); - } catch (ex) { - Cu.reportError(ex); - } - } - - if (isNew && !dataItem.newDownloadNotified) { - dataItem.newDownloadNotified = true; - this._notifyDownloadEvent("start"); - } - - // This is a final state of which we are only notified once. - if (dataItem.done) { - this._notifyDownloadEvent("finish"); - } - - // TODO Bug 830415: this isn't the right place to set these annotation. - // It should be set it in places' nsIDownloadHistory implementation. - if (!this._isPrivate && !dataItem.inProgress) { - let downloadMetaData = { state: dataItem.state, - endTime: dataItem.endTime }; - if (dataItem.done) - downloadMetaData.fileSize = dataItem.maxBytes; - - try { - PlacesUtils.annotations.setPageAnnotation( - NetUtil.newURI(dataItem.uri), "downloads/metaData", JSON.stringify(downloadMetaData), 0, - PlacesUtils.annotations.EXPIRE_WITH_HISTORY); - } - catch(ex) { - Cu.reportError(ex); - } - } - }, - - onProgressChange: function DD_onProgressChange(aWebProgress, aRequest, - aCurSelfProgress, - aMaxSelfProgress, - aCurTotalProgress, - aMaxTotalProgress, aDownload) - { - if (aDownload.isPrivate != this._isPrivate) { - // Ignore the downloads with a privacy status other than what we are - // tracking. - return; - } - - let dataItem = this._getOrAddDataItem(aDownload, false); - if (!dataItem) { - return; - } - - dataItem.currBytes = aDownload.amountTransferred; - dataItem.maxBytes = aDownload.size; - dataItem.speed = aDownload.speed; - dataItem.percentComplete = aDownload.percentComplete; - - this._views.forEach( - function (view) view.getViewItem(dataItem).onProgressChange() - ); - }, - - onStateChange: function () { }, - - onSecurityChange: function () { }, - - ////////////////////////////////////////////////////////////////////////////// - //// Notifications sent to the most recent browser window only - - /** - * Set to true after the first download causes the downloads panel to be - * displayed. - */ - get panelHasShownBefore() { - try { - return Services.prefs.getBoolPref("browser.download.panel.shown"); - } catch (ex) { } - return false; - }, - - set panelHasShownBefore(aValue) { - Services.prefs.setBoolPref("browser.download.panel.shown", aValue); - return aValue; - }, - - /** - * Displays a new or finished download notification in the most recent browser - * window, if one is currently available with the required privacy type. - * - * @param aType - * Set to "start" for new downloads, "finish" for completed downloads. - */ - _notifyDownloadEvent: function DD_notifyDownloadEvent(aType) - { - DownloadsCommon.log("Attempting to notify that a new download has started or finished."); - if (DownloadsCommon.useToolkitUI) { - DownloadsCommon.log("Cancelling notification - we're using the toolkit downloads manager."); - return; - } - - // Show the panel in the most recent browser window, if present. - let browserWin = RecentWindow.getMostRecentBrowserWindow({ private: this._isPrivate }); - if (!browserWin) { - return; - } - - if (this.panelHasShownBefore) { - // For new downloads after the first one, don't show the panel - // automatically, but provide a visible notification in the topmost - // browser window, if the status indicator is already visible. - DownloadsCommon.log("Showing new download notification."); - browserWin.DownloadsIndicatorView.showEventNotification(aType); - return; - } - this.panelHasShownBefore = true; - browserWin.DownloadsPanel.showPanel(); - } -}; - -XPCOMUtils.defineLazyGetter(this, "PrivateDownloadsData", function() { - return new DownloadsDataCtor(true); -}); - -XPCOMUtils.defineLazyGetter(this, "DownloadsData", function() { - return new DownloadsDataCtor(false); -}); - -//////////////////////////////////////////////////////////////////////////////// -//// DownloadsViewPrototype - -/** - * A prototype for an object that registers itself with DownloadsData as soon - * as a view is registered with it. - */ -const DownloadsViewPrototype = { - ////////////////////////////////////////////////////////////////////////////// - //// Registration of views - - /** - * Array of view objects that should be notified when the available status - * data changes. - * - * SUBCLASSES MUST OVERRIDE THIS PROPERTY. - */ - _views: null, - - /** - * Determines whether this view object is over the private or non-private - * downloads. - * - * SUBCLASSES MUST OVERRIDE THIS PROPERTY. - */ - _isPrivate: false, - - /** - * Adds an object to be notified when the available status data changes. - * The specified object is initialized with the currently available status. - * - * @param aView - * View object to be added. This reference must be - * passed to removeView before termination. - */ - addView: function DVP_addView(aView) - { - // Start receiving events when the first of our views is registered. - if (this._views.length == 0) { - if (this._isPrivate) { - PrivateDownloadsData.addView(this); - } else { - DownloadsData.addView(this); - } - } - - this._views.push(aView); - this.refreshView(aView); - }, - - /** - * Updates the properties of an object previously added using addView. - * - * @param aView - * View object to be updated. - */ - refreshView: function DVP_refreshView(aView) - { - // Update immediately even if we are still loading data asynchronously. - // Subclasses must provide these two functions! - this._refreshProperties(); - this._updateView(aView); - }, - - /** - * Removes an object previously added using addView. - * - * @param aView - * View object to be removed. - */ - removeView: function DVP_removeView(aView) - { - let index = this._views.indexOf(aView); - if (index != -1) { - this._views.splice(index, 1); - } - - // Stop receiving events when the last of our views is unregistered. - if (this._views.length == 0) { - if (this._isPrivate) { - PrivateDownloadsData.removeView(this); - } else { - DownloadsData.removeView(this); - } - } - }, - - ////////////////////////////////////////////////////////////////////////////// - //// Callback functions from DownloadsData - - /** - * Indicates whether we are still loading downloads data asynchronously. - */ - _loading: false, - - /** - * Called before multiple downloads are about to be loaded. - */ - onDataLoadStarting: function DVP_onDataLoadStarting() - { - this._loading = true; - }, - - /** - * Called after data loading finished. - */ - onDataLoadCompleted: function DVP_onDataLoadCompleted() - { - this._loading = false; - }, - - /** - * Called when the downloads database becomes unavailable (for example, we - * entered Private Browsing Mode and the database backend changed). - * References to existing data should be discarded. - * - * @note Subclasses should override this. - */ - onDataInvalidated: function DVP_onDataInvalidated() - { - throw Components.results.NS_ERROR_NOT_IMPLEMENTED; - }, - - /** - * Called when a new download data item is available, either during the - * asynchronous data load or when a new download is started. - * - * @param download - * Download object that was just added. - * @param newest - * When true, indicates that this item is the most recent and should be - * added in the topmost position. This happens when a new download is - * started. When false, indicates that the item is the least recent - * with regard to the items that have been already added. The latter - * generally happens during the asynchronous data load. - * - * @note Subclasses should override this. - */ - onDownloadAdded(download, newest) { - throw Components.results.NS_ERROR_NOT_IMPLEMENTED; - }, - - /** - * Called when the overall state of a Download has changed. In particular, - * this is called only once when the download succeeds or is blocked - * permanently, and is never called if only the current progress changed. - * - * The onDownloadChanged notification will always be sent afterwards. - * - * @note Subclasses should override this. - */ - onDownloadStateChanged(download) { - throw Components.results.NS_ERROR_NOT_IMPLEMENTED; - }, - - /** - * Called every time any state property of a Download may have changed, - * including progress properties. - * - * Note that progress notification changes are throttled at the Downloads.jsm - * API level, and there is no throttling mechanism in the front-end. - * - * @note Subclasses should override this. - */ - onDownloadChanged(download) { - throw Components.results.NS_ERROR_NOT_IMPLEMENTED; - }, - - /** - * Called when a data item is removed, ensures that the widget associated with - * the view item is removed from the user interface. - * - * @param download - * Download object that is being removed. - * - * @note Subclasses should override this. - */ - onDownloadRemoved(download) { - throw Components.results.NS_ERROR_NOT_IMPLEMENTED; - }, - - /** - * Private function used to refresh the internal properties being sent to - * each registered view. - * - * @note Subclasses should override this. - */ - _refreshProperties: function DID_refreshProperties() - { - throw Components.results.NS_ERROR_NOT_IMPLEMENTED; - }, - - /** - * Private function used to refresh an individual view. - * - * @note Subclasses should override this. - */ - _updateView: function DID_updateView() - { - throw Components.results.NS_ERROR_NOT_IMPLEMENTED; - } -}; - -//////////////////////////////////////////////////////////////////////////////// -//// DownloadsIndicatorData - -/** - * This object registers itself with DownloadsData as a view, and transforms the - * notifications it receives into overall status data, that is then broadcast to - * the registered download status indicators. - * - * Note that using this object does not automatically start the Download Manager - * service. Consumers will see an empty list of downloads until the service is - * actually started. This is useful to display a neutral progress indicator in - * the main browser window until the autostart timeout elapses. - */ -function DownloadsIndicatorDataCtor(aPrivate) { - this._isPrivate = aPrivate; - this._views = []; -} -DownloadsIndicatorDataCtor.prototype = { - __proto__: DownloadsViewPrototype, - - /** - * Removes an object previously added using addView. - * - * @param aView - * DownloadsIndicatorView object to be removed. - */ - removeView: function DID_removeView(aView) - { - DownloadsViewPrototype.removeView.call(this, aView); - - if (this._views.length == 0) { - this._itemCount = 0; - } - }, - - ////////////////////////////////////////////////////////////////////////////// - //// Callback functions from DownloadsData - - onDataLoadCompleted: function DID_onDataLoadCompleted() - { - DownloadsViewPrototype.onDataLoadCompleted.call(this); - this._updateViews(); - }, - - /** - * Called when the downloads database becomes unavailable (for example, we - * entered Private Browsing Mode and the database backend changed). - * References to existing data should be discarded. - */ - onDataInvalidated: function DID_onDataInvalidated() - { - this._itemCount = 0; - }, - - onDownloadAdded(download, newest) { - this._itemCount++; - this._updateViews(); - }, - - onDownloadStateChanged(download) { - if (download.succeeded || download.error) { - this.attention = true; - } - - // Since the state of a download changed, reset the estimated time left. - this._lastRawTimeLeft = -1; - this._lastTimeLeft = -1; - }, - - onDownloadChanged(download) { - this._updateViews(); - }, - - onDownloadRemoved(download) { - this._itemCount--; - this._updateViews(); - }, - - ////////////////////////////////////////////////////////////////////////////// - //// Propagation of properties to our views - - // The following properties are updated by _refreshProperties and are then - // propagated to the views. See _refreshProperties for details. - _hasDownloads: false, - _counter: "", - _percentComplete: -1, - _paused: false, - - /** - * Indicates whether the download indicators should be highlighted. - */ - set attention(aValue) - { - this._attention = aValue; - this._updateViews(); - return aValue; - }, - _attention: false, - - /** - * Indicates whether the user is interacting with downloads, thus the - * attention indication should not be shown even if requested. - */ - set attentionSuppressed(aValue) - { - this._attentionSuppressed = aValue; - this._attention = false; - this._updateViews(); - return aValue; - }, - _attentionSuppressed: false, - - /** - * Computes aggregate values and propagates the changes to our views. - */ - _updateViews: function DID_updateViews() - { - // Do not update the status indicators during batch loads of download items. - if (this._loading) { - return; - } - - this._refreshProperties(); - this._views.forEach(this._updateView, this); - }, - - /** - * Updates the specified view with the current aggregate values. - * - * @param aView - * DownloadsIndicatorView object to be updated. - */ - _updateView: function DID_updateView(aView) - { - aView.hasDownloads = this._hasDownloads; - aView.counter = this._counter; - aView.percentComplete = this._percentComplete; - aView.paused = this._paused; - aView.attention = this._attention && !this._attentionSuppressed; - }, - - ////////////////////////////////////////////////////////////////////////////// - //// Property updating based on current download status - - /** - * Number of download items that are available to be displayed. - */ - _itemCount: 0, - - /** - * Floating point value indicating the last number of seconds estimated until - * the longest download will finish. We need to store this value so that we - * don't continuously apply smoothing if the actual download state has not - * changed. This is set to -1 if the previous value is unknown. - */ - _lastRawTimeLeft: -1, - - /** - * Last number of seconds estimated until all in-progress downloads with a - * known size and speed will finish. This value is stored to allow smoothing - * in case of small variations. This is set to -1 if the previous value is - * unknown. - */ - _lastTimeLeft: -1, - - /** - * A generator function for the Download objects this summary is currently - * interested in. This generator is passed off to summarizeDownloads in order - * to generate statistics about the downloads we care about - in this case, - * it's all active downloads. - */ - * _activeDownloads() { - let downloads = this._isPrivate ? PrivateDownloadsData.downloads - : DownloadsData.downloads; - for (let download of downloads) { - if (!download.stopped || (download.canceled && download.hasPartialData)) { - yield download; - } - } - }, - - /** - * Computes aggregate values based on the current state of downloads. - */ - _refreshProperties: function DID_refreshProperties() - { - let summary = - DownloadsCommon.summarizeDownloads(this._activeDownloads()); - - // Determine if the indicator should be shown or get attention. - this._hasDownloads = (this._itemCount > 0); - - // If all downloads are paused, show the progress indicator as paused. - this._paused = summary.numActive > 0 && - summary.numActive == summary.numPaused; - - this._percentComplete = summary.percentComplete; - - // Display the estimated time left, if present. - if (summary.rawTimeLeft == -1) { - // There are no downloads with a known time left. - this._lastRawTimeLeft = -1; - this._lastTimeLeft = -1; - this._counter = ""; - } else { - // Compute the new time left only if state actually changed. - if (this._lastRawTimeLeft != summary.rawTimeLeft) { - this._lastRawTimeLeft = summary.rawTimeLeft; - this._lastTimeLeft = DownloadsCommon.smoothSeconds(summary.rawTimeLeft, - this._lastTimeLeft); - } - this._counter = DownloadsCommon.formatTimeLeft(this._lastTimeLeft); - } - } -}; - -XPCOMUtils.defineLazyGetter(this, "PrivateDownloadsIndicatorData", function() { - return new DownloadsIndicatorDataCtor(true); -}); - -XPCOMUtils.defineLazyGetter(this, "DownloadsIndicatorData", function() { - return new DownloadsIndicatorDataCtor(false); -}); - -//////////////////////////////////////////////////////////////////////////////// -//// DownloadsSummaryData - -/** - * DownloadsSummaryData is a view for DownloadsData that produces a summary - * of all downloads after a certain exclusion point aNumToExclude. For example, - * if there were 5 downloads in progress, and a DownloadsSummaryData was - * constructed with aNumToExclude equal to 3, then that DownloadsSummaryData - * would produce a summary of the last 2 downloads. - * - * @param aIsPrivate - * True if the browser window which owns the download button is a private - * window. - * @param aNumToExclude - * The number of items to exclude from the summary, starting from the - * top of the list. - */ -function DownloadsSummaryData(aIsPrivate, aNumToExclude) { - this._numToExclude = aNumToExclude; - // Since we can have multiple instances of DownloadsSummaryData, we - // override these values from the prototype so that each instance can be - // completely separated from one another. - this._loading = false; - - this._downloads = []; - - // Floating point value indicating the last number of seconds estimated until - // the longest download will finish. We need to store this value so that we - // don't continuously apply smoothing if the actual download state has not - // changed. This is set to -1 if the previous value is unknown. - this._lastRawTimeLeft = -1; - - // Last number of seconds estimated until all in-progress downloads with a - // known size and speed will finish. This value is stored to allow smoothing - // in case of small variations. This is set to -1 if the previous value is - // unknown. - this._lastTimeLeft = -1; - - // The following properties are updated by _refreshProperties and are then - // propagated to the views. - this._showingProgress = false; - this._details = ""; - this._description = ""; - this._numActive = 0; - this._percentComplete = -1; - - this._isPrivate = aIsPrivate; - this._views = []; -} - -DownloadsSummaryData.prototype = { - __proto__: DownloadsViewPrototype, - - /** - * Removes an object previously added using addView. - * - * @param aView - * DownloadsSummary view to be removed. - */ - removeView: function DSD_removeView(aView) - { - DownloadsViewPrototype.removeView.call(this, aView); - - if (this._views.length == 0) { - // Clear out our collection of Download objects. If we ever have - // another view registered with us, this will get re-populated. - this._downloads = []; - } - }, - - ////////////////////////////////////////////////////////////////////////////// - //// Callback functions from DownloadsData - see the documentation in - //// DownloadsViewPrototype for more information on what these functions - //// are used for. - - onDataLoadCompleted: function DSD_onDataLoadCompleted() - { - DownloadsViewPrototype.onDataLoadCompleted.call(this); - this._updateViews(); - }, - - onDataInvalidated: function DSD_onDataInvalidated() - { - this._dataItems = []; - }, - - onDownloadAdded(download, newest) { - if (newest) { - this._downloads.unshift(download); - } else { - this._downloads.push(download); - } - - this._updateViews(); - }, - - onDownloadStateChanged() { - // Since the state of a download changed, reset the estimated time left. - this._lastRawTimeLeft = -1; - this._lastTimeLeft = -1; - }, - - onDownloadChanged() { - this._updateViews(); - }, - - onDownloadRemoved(download) { - let itemIndex = this._downloads.indexOf(download); - this._downloads.splice(itemIndex, 1); - this._updateViews(); - }, - - ////////////////////////////////////////////////////////////////////////////// - //// Propagation of properties to our views - - /** - * Computes aggregate values and propagates the changes to our views. - */ - _updateViews: function DSD_updateViews() - { - // Do not update the status indicators during batch loads of download items. - if (this._loading) { - return; - } - - this._refreshProperties(); - this._views.forEach(this._updateView, this); - }, - - /** - * Updates the specified view with the current aggregate values. - * - * @param aView - * DownloadsIndicatorView object to be updated. - */ - _updateView: function DSD_updateView(aView) - { - aView.showingProgress = this._showingProgress; - aView.percentComplete = this._percentComplete; - aView.description = this._description; - aView.details = this._details; - }, - - ////////////////////////////////////////////////////////////////////////////// - //// Property updating based on current download status - - /** - * A generator function for the Download objects this summary is currently - * interested in. This generator is passed off to summarizeDownloads in order - * to generate statistics about the downloads we care about - in this case, - * it's the downloads in this._downloads after the first few to exclude, - * which was set when constructing this DownloadsSummaryData instance. - */ - * _downloadsForSummary() { - if (this._downloads.length > 0) { - for (let i = this._numToExclude; i < this._downloads.length; ++i) { - yield this._downloads[i]; - } - } - }, - - /** - * Computes aggregate values based on the current state of downloads. - */ - _refreshProperties: function DSD_refreshProperties() - { - // Pre-load summary with default values. - let summary = - DownloadsCommon.summarizeDownloads(this._downloadsForSummary()); - - this._description = DownloadsCommon.strings - .otherDownloads2(summary.numActive); - this._percentComplete = summary.percentComplete; - - // If all downloads are paused, show the progress indicator as paused. - this._showingProgress = summary.numDownloading > 0 || - summary.numPaused > 0; - - // Display the estimated time left, if present. - if (summary.rawTimeLeft == -1) { - // There are no downloads with a known time left. - this._lastRawTimeLeft = -1; - this._lastTimeLeft = -1; - this._details = ""; - } else { - // Compute the new time left only if state actually changed. - if (this._lastRawTimeLeft != summary.rawTimeLeft) { - this._lastRawTimeLeft = summary.rawTimeLeft; - this._lastTimeLeft = DownloadsCommon.smoothSeconds(summary.rawTimeLeft, - this._lastTimeLeft); - } - [this._details] = DownloadUtils.getDownloadStatusNoRate( - summary.totalTransferred, summary.totalSize, summary.slowestSpeed, - this._lastTimeLeft); - } - } -} diff --git a/components/downloads/DownloadsLogger.jsm b/components/downloads/DownloadsLogger.jsm deleted file mode 100644 index 1218539..0000000 --- a/components/downloads/DownloadsLogger.jsm +++ /dev/null @@ -1,76 +0,0 @@ -/* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil; -*- */ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * The contents of this file were copied almost entirely from - * toolkit/identity/LogUtils.jsm. Until we've got a more generalized logging - * mechanism for toolkit, I think this is going to be how we roll. - */ - -"use strict"; - -this.EXPORTED_SYMBOLS = ["DownloadsLogger"]; -const PREF_DEBUG = "browser.download.debug"; - -const Cu = Components.utils; -const Ci = Components.interfaces; -const Cc = Components.classes; -const Cr = Components.results; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -this.DownloadsLogger = { - _generateLogMessage: function _generateLogMessage(args) { - // create a string representation of a list of arbitrary things - let strings = []; - - for (let arg of args) { - if (typeof arg === 'string') { - strings.push(arg); - } else if (arg === undefined) { - strings.push('undefined'); - } else if (arg === null) { - strings.push('null'); - } else { - try { - strings.push(JSON.stringify(arg, null, 2)); - } catch(err) { - strings.push("<<something>>"); - } - } - }; - return 'Downloads: ' + strings.join(' '); - }, - - /** - * log() - utility function to print a list of arbitrary things - * - * Enable with about:config pref browser.download.debug - */ - log: function DL_log(...args) { - let output = this._generateLogMessage(args); - dump(output + "\n"); - - // Additionally, make the output visible in the Error Console - Services.console.logStringMessage(output); - }, - - /** - * reportError() - report an error through component utils as well as - * our log function - */ - reportError: function DL_reportError(...aArgs) { - // Report the error in the browser - let output = this._generateLogMessage(aArgs); - Cu.reportError(output); - dump("ERROR:" + output + "\n"); - for (let frame = Components.stack.caller; frame; frame = frame.caller) { - dump("\t" + frame + "\n"); - } - } - -}; diff --git a/components/downloads/DownloadsStartup.js b/components/downloads/DownloadsStartup.js deleted file mode 100644 index e1dd207..0000000 --- a/components/downloads/DownloadsStartup.js +++ /dev/null @@ -1,278 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * This component listens to notifications for startup, shutdown and session - * restore, controlling which downloads should be loaded from the database. - * - * To avoid affecting startup performance, this component monitors the current - * session restore state, but defers the actual downloads data manipulation - * until the Download Manager service is loaded. - */ - -"use strict"; - -//////////////////////////////////////////////////////////////////////////////// -//// Globals - -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; -const Cr = Components.results; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon", - "resource:///modules/DownloadsCommon.jsm"); -XPCOMUtils.defineLazyServiceGetter(this, "gSessionStartup", - "@mozilla.org/browser/sessionstartup;1", - "nsISessionStartup"); - -const kObservedTopics = [ - "sessionstore-windows-restored", - "sessionstore-browser-state-restored", - "download-manager-initialized", - "download-manager-change-retention", - "last-pb-context-exited", - "browser-lastwindow-close-granted", - "quit-application", - "profile-change-teardown", -]; - -/** - * CID of our implementation of nsIDownloadManagerUI. - */ -const kDownloadsUICid = Components.ID("{4d99321e-d156-455b-81f7-e7aa2308134f}"); - -/** - * Contract ID of the service implementing nsIDownloadManagerUI. - */ -const kDownloadsUIContractId = "@mozilla.org/download-manager-ui;1"; - -/** - * CID of the JavaScript implementation of nsITransfer. - */ -const kTransferCid = Components.ID("{1b4c85df-cbdd-4bb6-b04e-613caece083c}"); - -/** - * Contract ID of the service implementing nsITransfer. - */ -const kTransferContractId = "@mozilla.org/transfer;1"; - -//////////////////////////////////////////////////////////////////////////////// -//// DownloadsStartup - -function DownloadsStartup() { } - -DownloadsStartup.prototype = { - classID: Components.ID("{49507fe5-2cee-4824-b6a3-e999150ce9b8}"), - - _xpcom_factory: XPCOMUtils.generateSingletonFactory(DownloadsStartup), - - ////////////////////////////////////////////////////////////////////////////// - //// nsISupports - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, - Ci.nsISupportsWeakReference]), - - ////////////////////////////////////////////////////////////////////////////// - //// nsIObserver - - observe: function DS_observe(aSubject, aTopic, aData) - { - switch (aTopic) { - case "profile-after-change": - // Override Toolkit's nsIDownloadManagerUI implementation with our own. - // This must be done at application startup and not in the manifest to - // ensure that our implementation overrides the original one. - Components.manager.QueryInterface(Ci.nsIComponentRegistrar) - .registerFactory(kDownloadsUICid, "", - kDownloadsUIContractId, null); - - Components.manager.QueryInterface(Ci.nsIComponentRegistrar) - .registerFactory(kTransferCid, "", - kTransferContractId, null); - break; - - case "sessionstore-windows-restored": - case "sessionstore-browser-state-restored": - // Unless there is no saved session, there is a chance that we are - // starting up after a restart or a crash. We should check the disk - // database to see if there are completed downloads to recover and show - // in the panel, in addition to in-progress downloads. - if (gSessionStartup.sessionType != Ci.nsISessionStartup.NO_SESSION) { - this._restoringSession = true; - } - this._ensureDataLoaded(); - break; - - case "download-manager-initialized": - // Don't initialize the JavaScript data and user interface layer if we - // are initializing the Download Manager service during shutdown. - if (this._shuttingDown) { - break; - } - - // Start receiving events for active and new downloads before we return - // from this observer function. We can't defer the execution of this - // step, to ensure that we don't lose events raised in the meantime. - DownloadsCommon.initializeAllDataLinks( - aSubject.QueryInterface(Ci.nsIDownloadManager)); - - this._downloadsServiceInitialized = true; - - // Since this notification is generated during the getService call and - // we need to get the Download Manager service ourselves, we must post - // the handler on the event queue to be executed later. - Services.tm.mainThread.dispatch(this._ensureDataLoaded.bind(this), - Ci.nsIThread.DISPATCH_NORMAL); - break; - - case "download-manager-change-retention": - // If we're using the Downloads Panel, we override the retention - // preference to always retain downloads on completion. - if (!DownloadsCommon.useToolkitUI) { - aSubject.QueryInterface(Ci.nsISupportsPRInt32).data = 2; - } - break; - - case "browser-lastwindow-close-granted": - // When using the panel interface, downloads that are already completed - // should be removed when the last full browser window is closed. This - // event is invoked only if the application is not shutting down yet. - // If the Download Manager service is not initialized, we don't want to - // initialize it just to clean up completed downloads, because they can - // be present only in case there was a browser crash or restart. - if (this._downloadsServiceInitialized && - !DownloadsCommon.useToolkitUI) { - Services.downloads.cleanUp(); - } - break; - - case "last-pb-context-exited": - // Similar to the above notification, but for private downloads. - if (this._downloadsServiceInitialized && - !DownloadsCommon.useToolkitUI) { - Services.downloads.cleanUpPrivate(); - } - break; - - case "quit-application": - // When the application is shutting down, we must free all resources in - // addition to cleaning up completed downloads. If the Download Manager - // service is not initialized, we don't want to initialize it just to - // clean up completed downloads, because they can be present only in - // case there was a browser crash or restart. - this._shuttingDown = true; - if (!this._downloadsServiceInitialized) { - break; - } - - DownloadsCommon.terminateAllDataLinks(); - - // When using the panel interface, downloads that are already completed - // should be removed when quitting the application. - if (!DownloadsCommon.useToolkitUI && aData != "restart") { - this._cleanupOnShutdown = true; - } - break; - - case "profile-change-teardown": - // If we need to clean up, we must do it synchronously after all the - // "quit-application" listeners are invoked, so that the Download - // Manager service has a chance to pause or cancel in-progress downloads - // before we remove completed downloads from the list. Note that, since - // "quit-application" was invoked, we've already exited Private Browsing - // Mode, thus we are always working on the disk database. - if (this._cleanupOnShutdown) { - Services.downloads.cleanUp(); - } - - if (!DownloadsCommon.useToolkitUI) { - // If we got this far, that means that we finished our first session - // with the Downloads Panel without crashing. This means that we don't - // have to force displaying only active downloads on the next startup - // now. - this._firstSessionCompleted = true; - } - break; - } - }, - - ////////////////////////////////////////////////////////////////////////////// - //// Private - - /** - * Indicates whether we're restoring a previous session. This is used by - * _recoverAllDownloads to determine whether or not we should load and - * display all downloads data, or restrict it to only the active downloads. - */ - _restoringSession: false, - - /** - * Indicates whether the Download Manager service has been initialized. This - * flag is required because we want to avoid accessing the service immediately - * at browser startup. The service will start when the user first requests a - * download, or some time after browser startup. - */ - _downloadsServiceInitialized: false, - - /** - * True while we are processing the "quit-application" event, and later. - */ - _shuttingDown: false, - - /** - * True during shutdown if we need to remove completed downloads. - */ - _cleanupOnShutdown: false, - - /** - * True if we should display all downloads, as opposed to just active - * downloads. We decide to display all downloads if we're restoring a session, - * or if we're using the Downloads Panel anytime after the first session with - * it has completed. - */ - get _recoverAllDownloads() { - return this._restoringSession || - (!DownloadsCommon.useToolkitUI && this._firstSessionCompleted); - }, - - /** - * True if we've ever completed a session with the Downloads Panel enabled. - */ - get _firstSessionCompleted() { - return Services.prefs - .getBoolPref("browser.download.panel.firstSessionCompleted"); - }, - - set _firstSessionCompleted(aValue) { - Services.prefs.setBoolPref("browser.download.panel.firstSessionCompleted", - aValue); - return aValue; - }, - - /** - * Ensures that persistent download data is reloaded at the appropriate time. - */ - _ensureDataLoaded: function DS_ensureDataLoaded() - { - if (!this._downloadsServiceInitialized) { - return; - } - - // If the previous session has been already restored, then we ensure that - // all the downloads are loaded. Otherwise, we only ensure that the active - // downloads from the previous session are loaded. - DownloadsCommon.ensureAllPersistentDataLoaded(!this._recoverAllDownloads); - } -}; - -//////////////////////////////////////////////////////////////////////////////// -//// Module - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DownloadsStartup]); diff --git a/components/downloads/DownloadsTaskbar.jsm b/components/downloads/DownloadsTaskbar.jsm deleted file mode 100644 index cf915ab..0000000 --- a/components/downloads/DownloadsTaskbar.jsm +++ /dev/null @@ -1,177 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Handles the download progress indicator in the taskbar. - */ - -"use strict"; - -this.EXPORTED_SYMBOLS = [ - "DownloadsTaskbar", -]; - -//////////////////////////////////////////////////////////////////////////////// -//// Globals - -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; -const Cr = Components.results; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "Downloads", - "resource://gre/modules/Downloads.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow", - "resource:///modules/RecentWindow.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Services", - "resource://gre/modules/Services.jsm"); - -XPCOMUtils.defineLazyGetter(this, "gWinTaskbar", function () { - if (!("@mozilla.org/windows-taskbar;1" in Cc)) { - return null; - } - let winTaskbar = Cc["@mozilla.org/windows-taskbar;1"] - .getService(Ci.nsIWinTaskbar); - return winTaskbar.available && winTaskbar; -}); - -XPCOMUtils.defineLazyGetter(this, "gMacTaskbarProgress", function () { - return ("@mozilla.org/widget/macdocksupport;1" in Cc) && - Cc["@mozilla.org/widget/macdocksupport;1"] - .getService(Ci.nsITaskbarProgress); -}); - -//////////////////////////////////////////////////////////////////////////////// -//// DownloadsTaskbar - -/** - * Handles the download progress indicator in the taskbar. - */ -this.DownloadsTaskbar = { - /** - * Underlying DownloadSummary providing the aggregate download information, or - * null if the indicator has never been initialized. - */ - _summary: null, - - /** - * nsITaskbarProgress object to which download information is dispatched. - * This can be null if the indicator has never been initialized or if the - * indicator is currently hidden on Windows. - */ - _taskbarProgress: null, - - /** - * This method is called after a new browser window is opened, and ensures - * that the download progress indicator is displayed in the taskbar. - * - * On Windows, the indicator is attached to the first browser window that - * calls this method. When the window is closed, the indicator is moved to - * another browser window, if available, in no particular order. When there - * are no browser windows visible, the indicator is hidden. - * - * On Mac OS X, the indicator is initialized globally when this method is - * called for the first time. Subsequent calls have no effect. - * - * @param aBrowserWindow - * nsIDOMWindow object of the newly opened browser window to which the - * indicator may be attached. - */ - registerIndicator(aBrowserWindow) { - if (!this._taskbarProgress) { - if (gMacTaskbarProgress) { - // On Mac OS X, we have to register the global indicator only once. - this._taskbarProgress = gMacTaskbarProgress; - // Free the XPCOM reference on shutdown, to prevent detecting a leak. - Services.obs.addObserver(() => { - this._taskbarProgress = null; - gMacTaskbarProgress = null; - }, "quit-application-granted", false); - } else if (gWinTaskbar) { - // On Windows, the indicator is currently hidden because we have no - // previous browser window, thus we should attach the indicator now. - this._attachIndicator(aBrowserWindow); - } else { - // The taskbar indicator is not available on this platform. - return; - } - } - - // Ensure that the DownloadSummary object will be created asynchronously. - if (!this._summary) { - Downloads.getSummary(Downloads.ALL).then(summary => { - // In case the method is re-entered, we simply ignore redundant - // invocations of the callback, instead of keeping separate state. - if (this._summary) { - return; - } - this._summary = summary; - return this._summary.addView(this); - }).then(null, Cu.reportError); - } - }, - - /** - * On Windows, attaches the taskbar indicator to the specified browser window. - */ - _attachIndicator(aWindow) { - // Activate the indicator on the specified window. - let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIXULWindow).docShell; - this._taskbarProgress = gWinTaskbar.getTaskbarProgress(docShell); - - // If the DownloadSummary object has already been created, we should update - // the state of the new indicator, otherwise it will be updated as soon as - // the DownloadSummary view is registered. - if (this._summary) { - this.onSummaryChanged(); - } - - aWindow.addEventListener("unload", () => { - // Locate another browser window, excluding the one being closed. - let browserWindow = RecentWindow.getMostRecentBrowserWindow(); - if (browserWindow) { - // Move the progress indicator to the other browser window. - this._attachIndicator(browserWindow); - } else { - // The last browser window has been closed. We remove the reference to - // the taskbar progress object so that the indicator will be registered - // again on the next browser window that is opened. - this._taskbarProgress = null; - } - }, false); - }, - - ////////////////////////////////////////////////////////////////////////////// - //// DownloadSummary view - - onSummaryChanged() { - // If the last browser window has been closed, we have no indicator any more. - if (!this._taskbarProgress) { - return; - } - - if (this._summary.allHaveStopped || this._summary.progressTotalBytes == 0) { - this._taskbarProgress.setProgressState( - Ci.nsITaskbarProgress.STATE_NO_PROGRESS, 0, 0); - } else { - // For a brief moment before completion, some download components may - // report more transferred bytes than the total number of bytes. Thus, - // ensure that we never break the expectations of the progress indicator. - let progressCurrentBytes = Math.min(this._summary.progressTotalBytes, - this._summary.progressCurrentBytes); - this._taskbarProgress.setProgressState( - Ci.nsITaskbarProgress.STATE_NORMAL, - progressCurrentBytes, - this._summary.progressTotalBytes); - } - }, -}; diff --git a/components/downloads/DownloadsUI.js b/components/downloads/DownloadsUI.js deleted file mode 100644 index afdbda8..0000000 --- a/components/downloads/DownloadsUI.js +++ /dev/null @@ -1,151 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * This component implements the nsIDownloadManagerUI interface and opens the - * downloads panel in the most recent browser window when requested. - * - * If a specific preference is set, this component transparently forwards all - * calls to the original implementation in Toolkit, that shows the window UI. - */ - -"use strict"; - -//////////////////////////////////////////////////////////////////////////////// -//// Globals - -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; -const Cr = Components.results; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon", - "resource:///modules/DownloadsCommon.jsm"); -XPCOMUtils.defineLazyServiceGetter(this, "gBrowserGlue", - "@mozilla.org/browser/browserglue;1", - "nsIBrowserGlue"); -XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow", - "resource:///modules/RecentWindow.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", - "resource://gre/modules/PrivateBrowsingUtils.jsm"); - -//////////////////////////////////////////////////////////////////////////////// -//// DownloadsUI - -function DownloadsUI() -{ - XPCOMUtils.defineLazyGetter(this, "_toolkitUI", function () { - // Create Toolkit's nsIDownloadManagerUI implementation. - return Components.classesByID["{7dfdf0d1-aff6-4a34-bad1-d0fe74601642}"] - .getService(Ci.nsIDownloadManagerUI); - }); -} - -DownloadsUI.prototype = { - classID: Components.ID("{4d99321e-d156-455b-81f7-e7aa2308134f}"), - - _xpcom_factory: XPCOMUtils.generateSingletonFactory(DownloadsUI), - - ////////////////////////////////////////////////////////////////////////////// - //// nsISupports - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDownloadManagerUI]), - - ////////////////////////////////////////////////////////////////////////////// - //// nsIDownloadManagerUI - - show: function DUI_show(aWindowContext, aDownload, aReason, aUsePrivateUI) - { - if (DownloadsCommon.useToolkitUI && !PrivateBrowsingUtils.isWindowPrivate(aWindowContext)) { - this._toolkitUI.show(aWindowContext, aDownload, aReason, aUsePrivateUI); - return; - } - - if (!aReason) { - aReason = Ci.nsIDownloadManagerUI.REASON_USER_INTERACTED; - } - - if (aReason == Ci.nsIDownloadManagerUI.REASON_NEW_DOWNLOAD) { - const kMinimized = Ci.nsIDOMChromeWindow.STATE_MINIMIZED; - let browserWin = gBrowserGlue.getMostRecentBrowserWindow(); - - if (!browserWin || browserWin.windowState == kMinimized) { - this._showDownloadManagerUI(aWindowContext, aUsePrivateUI); - } - else { - // If the indicator is visible, then new download notifications are - // already handled by the panel service. - browserWin.DownloadsButton.checkIsVisible(function(isVisible) { - if (!isVisible) { - this._showDownloadManagerUI(aWindowContext, aUsePrivateUI); - } - }.bind(this)); - } - } else { - this._showDownloadManagerUI(aWindowContext, aUsePrivateUI); - } - }, - - get visible() - { - // If we're still using the toolkit downloads manager, delegate the call - // to it. Otherwise, return true for now, until we decide on how we want - // to indicate that a new download has started if a browser window is - // not available or minimized. - return DownloadsCommon.useToolkitUI ? this._toolkitUI.visible : true; - }, - - getAttention: function DUI_getAttention() - { - if (DownloadsCommon.useToolkitUI) { - this._toolkitUI.getAttention(); - } - }, - - /** - * Helper function that opens the download manager UI. - */ - _showDownloadManagerUI: - function DUI_showDownloadManagerUI(aWindowContext, aUsePrivateUI) - { - // If we weren't given a window context, try to find a browser window - // to use as our parent - and if that doesn't work, error out and give up. - let parentWindow = aWindowContext; - if (!parentWindow) { - parentWindow = RecentWindow.getMostRecentBrowserWindow({ private: !!aUsePrivateUI }); - if (!parentWindow) { - Components.utils.reportError( - "Couldn't find a browser window to open the Places Downloads View " + - "from."); - return; - } - } - - // If window is private then show it in a tab. - if (PrivateBrowsingUtils.isWindowPrivate(parentWindow)) { - parentWindow.openUILinkIn("about:downloads", "tab"); - return; - } else { - let organizer = Services.wm.getMostRecentWindow("Places:Organizer"); - if (!organizer) { - parentWindow.openDialog("chrome://browser/content/places/places.xul", - "", "chrome,toolbar=yes,dialog=no,resizable", - "Downloads"); - } else { - organizer.PlacesOrganizer.selectLeftPaneQuery("Downloads"); - organizer.focus(); - } - } - } -}; - -//////////////////////////////////////////////////////////////////////////////// -//// Module - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DownloadsUI]); diff --git a/components/downloads/DownloadsViewUI.jsm b/components/downloads/DownloadsViewUI.jsm deleted file mode 100644 index ede593e..0000000 --- a/components/downloads/DownloadsViewUI.jsm +++ /dev/null @@ -1,250 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* - * This module is imported by code that uses the "download.xml" binding, and - * provides prototypes for objects that handle input and display information. - */ - -"use strict"; - -this.EXPORTED_SYMBOLS = [ - "DownloadsViewUI", -]; - -const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils", - "resource://gre/modules/DownloadUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon", - "resource:///modules/DownloadsCommon.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "OS", - "resource://gre/modules/osfile.jsm"); - -this.DownloadsViewUI = {}; - -/** - * A download element shell is responsible for handling the commands and the - * displayed data for a single element that uses the "download.xml" binding. - * - * The information to display is obtained through the associated Download object - * from the JavaScript API for downloads, and commands are executed using a - * combination of Download methods and DownloadsCommon.jsm helper functions. - * - * Specialized versions of this shell must be defined, and they are required to - * implement the "download" property or getter. Currently these objects are the - * HistoryDownloadElementShell and the DownloadsViewItem for the panel. The - * history view may use a HistoryDownload object in place of a Download object. - */ -this.DownloadsViewUI.DownloadElementShell = function () {} - -this.DownloadsViewUI.DownloadElementShell.prototype = { - /** - * The richlistitem for the download, initialized by the derived object. - */ - element: null, - - /** - * URI string for the file type icon displayed in the download element. - */ - get image() { - if (!this.download.target.path) { - // Old history downloads may not have a target path. - return "moz-icon://.unknown?size=32"; - } - - // When a download that was previously in progress finishes successfully, it - // means that the target file now exists and we can extract its specific - // icon, for example from a Windows executable. To ensure that the icon is - // reloaded, however, we must change the URI used by the XUL image element, - // for example by adding a query parameter. This only works if we add one of - // the parameters explicitly supported by the nsIMozIconURI interface. - return "moz-icon://" + this.download.target.path + "?size=32" + - (this.download.succeeded ? "&state=normal" : ""); - }, - - /** - * The user-facing label for the download. This is normally the leaf name of - * the download target file. In case this is a very old history download for - * which the target file is unknown, the download source URI is displayed. - */ - get displayName() { - if (!this.download.target.path) { - return this.download.source.url; - } - return OS.Path.basename(this.download.target.path); - }, - - get extendedDisplayName() { - let s = DownloadsCommon.strings; - let referrer = this.download.source.referrer || - this.download.source.url; - let [displayHost, fullHost] = DownloadUtils.getURIHost(referrer); - return s.statusSeparator(this.displayName, displayHost); - }, - - get extendedDisplayNameTip() { - let s = DownloadsCommon.strings; - let referrer = this.download.source.referrer || - this.download.source.url; - let [displayHost, fullHost] = DownloadUtils.getURIHost(referrer); - return s.statusSeparator(this.displayName, fullHost); - }, - - /** - * The progress element for the download, or undefined in case the XBL binding - * has not been applied yet. - */ - get _progressElement() { - if (!this.__progressElement) { - // If the element is not available now, we will try again the next time. - this.__progressElement = - this.element.ownerDocument.getAnonymousElementByAttribute( - this.element, "anonid", - "progressmeter"); - } - return this.__progressElement; - }, - - /** - * Processes a major state change in the user interface, then proceeds with - * the normal progress update. This function is not called for every progress - * update in order to improve performance. - */ - _updateState() { - this.element.setAttribute("displayName", this.displayName); - this.element.setAttribute("extendedDisplayName", this.extendedDisplayName); - this.element.setAttribute("extendedDisplayNameTip", this.extendedDisplayNameTip); - this.element.setAttribute("image", this.image); - this.element.setAttribute("state", - DownloadsCommon.stateOfDownload(this.download)); - - // Since state changed, reset the time left estimation. - this.lastEstimatedSecondsLeft = Infinity; - - this._updateProgress(); - }, - - /** - * Updates the elements that change regularly for in-progress downloads, - * namely the progress bar and the status line. - */ - _updateProgress() { - if (this.download.succeeded) { - // We only need to add or remove this attribute for succeeded downloads. - if (this.download.target.exists) { - this.element.setAttribute("exists", "true"); - } else { - this.element.removeAttribute("exists"); - } - } - - // The progress bar is only displayed for in-progress downloads. - if (this.download.hasProgress) { - this.element.setAttribute("progressmode", "normal"); - this.element.setAttribute("progress", this.download.progress); - } else { - this.element.setAttribute("progressmode", "undetermined"); - } - - // Dispatch the ValueChange event for accessibility, if possible. - if (this._progressElement) { - let event = this.element.ownerDocument.createEvent("Events"); - event.initEvent("ValueChange", true, true); - this._progressElement.dispatchEvent(event); - } - - let status = this.statusTextAndTip; - this.element.setAttribute("status", status.text); - this.element.setAttribute("statusTip", status.tip); - }, - - lastEstimatedSecondsLeft: Infinity, - - /** - * Returns the text for the status line and the associated tooltip. These are - * returned by a single property because they are computed together. The - * result may be overridden by derived objects. - */ - get statusTextAndTip() this.rawStatusTextAndTip, - - /** - * Derived objects may call this to get the status text. - */ - get rawStatusTextAndTip() { - const nsIDM = Ci.nsIDownloadManager; - let s = DownloadsCommon.strings; - - let text = ""; - let tip = ""; - - if (!this.download.stopped) { - let totalBytes = this.download.hasProgress ? this.download.totalBytes - : -1; - // By default, extended status information including the individual - // download rate is displayed in the tooltip. The history view overrides - // the getter and displays the datails in the main area instead. - [text] = DownloadUtils.getDownloadStatusNoRate( - this.download.currentBytes, - totalBytes, - this.download.speed, - this.lastEstimatedSecondsLeft); - let newEstimatedSecondsLeft; - [tip, newEstimatedSecondsLeft] = DownloadUtils.getDownloadStatus( - this.download.currentBytes, - totalBytes, - this.download.speed, - this.lastEstimatedSecondsLeft); - this.lastEstimatedSecondsLeft = newEstimatedSecondsLeft; - } else if (this.download.canceled && this.download.hasPartialData) { - let totalBytes = this.download.hasProgress ? this.download.totalBytes - : -1; - let transfer = DownloadUtils.getTransferTotal(this.download.currentBytes, - totalBytes); - - // We use the same XUL label to display both the state and the amount - // transferred, for example "Paused - 1.1 MB". - text = s.statusSeparatorBeforeNumber(s.statePaused, transfer); - } else if (!this.download.succeeded && !this.download.canceled && - !this.download.error) { - text = s.stateStarting; - } else { - let stateLabel; - - if (this.download.succeeded) { - // For completed downloads, show the file size (e.g. "1.5 MB"). - if (this.download.target.size !== undefined) { - let [size, unit] = - DownloadUtils.convertByteUnits(this.download.target.size); - stateLabel = s.sizeWithUnits(size, unit); - } else { - // History downloads may not have a size defined. - stateLabel = s.sizeUnknown; - } - } else if (this.download.canceled) { - stateLabel = s.stateCanceled; - } else if (this.download.error.becauseBlockedByParentalControls) { - stateLabel = s.stateBlockedParentalControls; - } else if (this.download.error.becauseBlockedByReputationCheck) { - stateLabel = s.stateDirty; - } else { - stateLabel = s.stateFailed; - } - - let referrer = this.download.source.referrer || this.download.source.url; - let [displayHost, fullHost] = DownloadUtils.getURIHost(referrer); - - let date = new Date(this.download.endTime); - let [displayDate, fullDate] = DownloadUtils.getReadableDates(date); - - let firstPart = s.statusSeparator(stateLabel, displayHost); - text = s.statusSeparator(firstPart, displayDate); - tip = s.statusSeparator(fullHost, fullDate); - } - - return { text, tip: tip || text }; - }, -}; diff --git a/components/downloads/content/allDownloadsViewOverlay.css b/components/downloads/content/allDownloadsViewOverlay.css deleted file mode 100644 index c062ae4..0000000 --- a/components/downloads/content/allDownloadsViewOverlay.css +++ /dev/null @@ -1,56 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * The downloads richlistbox may list thousands of items, and it turns out - * XBL binding attachment, and even more so detachment, is a performance hog. - * This hack makes sure we don't apply any binding to inactive items (inactive - * items are history downloads that haven't been in the visible area). - * We can do this because the richlistbox implementation does not interact - * much with the richlistitem binding. However, this may turn out to have - * some side effects (see bug 828111 for the details). - * - * We might be able to do away with this workaround once bug 653881 is fixed. - */ -richlistitem.download { - -moz-binding: none; -} - -richlistitem.download[active] { - -moz-binding: url('chrome://browser/content/downloads/download.xml#download-full-ui'); -} - -richlistitem.download[active]:-moz-any([state="-1"],/* Starting (initial) */ - [state="0"], /* Downloading */ - [state="4"], /* Paused */ - [state="5"], /* Starting (queued) */ - [state="7"]) /* Scanning */ -{ - -moz-binding: url('chrome://browser/content/downloads/download.xml#download-in-progress-full-ui'); -} - -.download-state:not( [state="0"] /* Downloading */) - .downloadPauseMenuItem, -.download-state:not( [state="4"] /* Paused */) - .downloadResumeMenuItem, -.download-state:not(:-moz-any([state="2"], /* Failed */ - [state="4"]) /* Paused */) - .downloadCancelMenuItem, -.download-state[state]:not(:-moz-any([state="1"], /* Finished */ - [state="2"], /* Failed */ - [state="3"], /* Canceled */ - [state="6"], /* Blocked (parental) */ - [state="8"], /* Blocked (dirty) */ - [state="9"]) /* Blocked (policy) */) - .downloadRemoveFromHistoryMenuItem, -.download-state:not(:-moz-any([state="-1"],/* Starting (initial) */ - [state="0"], /* Downloading */ - [state="1"], /* Finished */ - [state="4"], /* Paused */ - [state="5"]) /* Starting (queued) */) - .downloadShowMenuItem, -.download-state[state="7"] /* Scanning */ .downloadCommandsSeparator -{ - display: none; -} diff --git a/components/downloads/content/allDownloadsViewOverlay.js b/components/downloads/content/allDownloadsViewOverlay.js deleted file mode 100644 index 4830f21..0000000 --- a/components/downloads/content/allDownloadsViewOverlay.js +++ /dev/null @@ -1,1399 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils", - "resource://gre/modules/DownloadUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon", - "resource:///modules/DownloadsCommon.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "DownloadsViewUI", - "resource:///modules/DownloadsViewUI.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", - "resource://gre/modules/FileUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", - "resource://gre/modules/NetUtil.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "OS", - "resource://gre/modules/osfile.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", - "resource://gre/modules/PlacesUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Promise", - "resource://gre/modules/Promise.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow", - "resource:///modules/RecentWindow.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Services", - "resource://gre/modules/Services.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Task", - "resource://gre/modules/Task.jsm"); - -const nsIDM = Ci.nsIDownloadManager; - -const DESTINATION_FILE_URI_ANNO = "downloads/destinationFileURI"; -const DOWNLOAD_META_DATA_ANNO = "downloads/metaData"; - -const DOWNLOAD_VIEW_SUPPORTED_COMMANDS = - ["cmd_delete", "cmd_copy", "cmd_paste", "cmd_selectAll", - "downloadsCmd_pauseResume", "downloadsCmd_cancel", - "downloadsCmd_open", "downloadsCmd_show", "downloadsCmd_retry", - "downloadsCmd_openReferrer", "downloadsCmd_clearDownloads"]; - -/** - * Represents a download from the browser history. It implements part of the - * interface of the Download object. - * - * @param aPlacesNode - * The Places node from which the history download should be initialized. - */ -function HistoryDownload(aPlacesNode) { - // TODO (bug 829201): history downloads should get the referrer from Places. - this.source = { - url: aPlacesNode.uri, - }; - this.target = { - path: undefined, - exists: false, - size: undefined, - }; - - // In case this download cannot obtain its end time from the Places metadata, - // use the time from the Places node, that is the start time of the download. - this.endTime = aPlacesNode.time / 1000; -} - -HistoryDownload.prototype = { - /** - * Pushes information from Places metadata into this object. - */ - updateFromMetaData(metaData) { - try { - this.target.path = Cc["@mozilla.org/network/protocol;1?name=file"] - .getService(Ci.nsIFileProtocolHandler) - .getFileFromURLSpec(metaData.targetFileSpec).path; - } catch (ex) { - this.target.path = undefined; - } - - if ("state" in metaData) { - this.succeeded = metaData.state == nsIDM.DOWNLOAD_FINISHED; - this.error = metaData.state == nsIDM.DOWNLOAD_FAILED - ? { message: "History download failed." } - : metaData.state == nsIDM.DOWNLOAD_BLOCKED_PARENTAL - ? { becauseBlockedByParentalControls: true } - : metaData.state == nsIDM.DOWNLOAD_DIRTY - ? { becauseBlockedByReputationCheck: true } - : null; - this.canceled = metaData.state == nsIDM.DOWNLOAD_CANCELED || - metaData.state == nsIDM.DOWNLOAD_PAUSED; - this.endTime = metaData.endTime; - - // Normal history downloads are assumed to exist until the user interface - // is refreshed, at which point these values may be updated. - this.target.exists = true; - this.target.size = metaData.fileSize; - } else { - // Metadata might be missing from a download that has started but hasn't - // stopped already. Normally, this state is overridden with the one from - // the corresponding in-progress session download. But if the browser is - // terminated abruptly and additionally the file with information about - // in-progress downloads is lost, we may end up using this state. We use - // the failed state to allow the download to be restarted. - // - // On the other hand, if the download is missing the target file - // annotation as well, it is just a very old one, and we can assume it - // succeeded. - this.succeeded = !this.target.path; - this.error = this.target.path ? { message: "Unstarted download." } : null; - this.canceled = false; - - // These properties may be updated if the user interface is refreshed. - this.target.exists = false; - this.target.size = undefined; - } - }, - - /** - * History downloads are never in progress. - */ - stopped: true, - - /** - * No percentage indication is shown for history downloads. - */ - hasProgress: false, - - /** - * History downloads cannot be restarted using their partial data, even if - * they are indicated as paused in their Places metadata. The only way is to - * use the information from a persisted session download, that will be shown - * instead of the history download. In case this session download is not - * available, we show the history download as canceled, not paused. - */ - hasPartialData: false, - - /** - * This method mimicks the "start" method of session downloads, and is called - * when the user retries a history download. - * - * At present, we always ask the user for a new target path when retrying a - * history download. In the future we may consider reusing the known target - * path if the folder still exists and the file name is not already used, - * except when the user preferences indicate that the target path should be - * requested every time a new download is started. - */ - start() { - let browserWin = RecentWindow.getMostRecentBrowserWindow(); - let initiatingDoc = browserWin ? browserWin.document : document; - - // Do not suggest a file name if we don't know the original target. - let leafName = this.target.path ? OS.Path.basename(this.target.path) : null; - DownloadURL(this.source.url, leafName, initiatingDoc); - - return Promise.resolve(); - }, - - /** - * This method mimicks the "refresh" method of session downloads, except that - * it cannot notify that the data changed to the Downloads View. - */ - refresh: Task.async(function* () { - try { - this.target.size = (yield OS.File.stat(this.target.path)).size; - this.target.exists = true; - } catch (ex) { - // We keep the known file size from the metadata, if any. - this.target.exists = false; - } - }), -}; - -/** - * A download element shell is responsible for handling the commands and the - * displayed data for a single download view element. - * - * The shell may contain a session download, a history download, or both. When - * both a history and a session download are present, the session download gets - * priority and its information is displayed. - * - * On construction, a new richlistitem is created, and can be accessed through - * the |element| getter. The shell doesn't insert the item in a richlistbox, the - * caller must do it and remove the element when it's no longer needed. - * - * The caller is also responsible for forwarding status notifications for - * session downloads, calling the onStateChanged and onChanged methods. - * - * @param [optional] aSessionDownload - * The session download, required if aHistoryDownload is not set. - * @param [optional] aHistoryDownload - * The history download, required if aSessionDownload is not set. - */ -function HistoryDownloadElementShell(aSessionDownload, aHistoryDownload) { - this.element = document.createElement("richlistitem"); - this.element._shell = this; - - this.element.classList.add("download"); - this.element.classList.add("download-state"); - - if (aSessionDownload) { - this.sessionDownload = aSessionDownload; - } - if (aHistoryDownload) { - this.historyDownload = aHistoryDownload; - } -} - -HistoryDownloadElementShell.prototype = { - __proto__: DownloadsViewUI.DownloadElementShell.prototype, - - /** - * Manages the "active" state of the shell. By default all the shells without - * a session download are inactive, thus their UI is not updated. They must - * be activated when entering the visible area. Session downloads are always - * active. - */ - ensureActive: function DES_ensureActive() { - if (!this._active) { - this._active = true; - this.element.setAttribute("active", true); - this._updateUI(); - } - }, - get active() !!this._active, - - /** - * Overrides the base getter to return the Download or HistoryDownload object - * for displaying information and executing commands in the user interface. - */ - get download() this._sessionDownload || this._historyDownload, - - _sessionDownload: null, - get sessionDownload() this._sessionDownload, - set sessionDownload(aValue) { - if (this._sessionDownload != aValue) { - if (!aValue && !this._historyDownload) { - throw new Error("Should always have either a Download or a HistoryDownload"); - } - - this._sessionDownload = aValue; - - this.ensureActive(); - this._updateUI(); - } - return aValue; - }, - - _historyDownload: null, - get historyDownload() this._historyDownload, - set historyDownload(aValue) { - if (this._historyDownload != aValue) { - if (!aValue && !this._sessionDownload) { - throw new Error("Should always have either a Download or a HistoryDownload"); - } - - this._historyDownload = aValue; - - // We don't need to update the UI if we had a session data item, because - // the places information isn't used in this case. - if (!this._sessionDownload) { - this._updateUI(); - } - } - return aValue; - }, - - _updateUI() { - // There is nothing to do if the item has always been invisible. - if (!this.active) { - return; - } - - // Since the state changed, we may need to check the target file again. - this._targetFileChecked = false; - - this._updateState(); - }, - - get statusTextAndTip() { - let status = this.rawStatusTextAndTip; - - // The base object would show extended progress information in the tooltip, - // but we move this to the main view and never display a tooltip. - if (!this.download.stopped) { - status.text = status.tip; - } - status.tip = ""; - - return status; - }, - - onStateChanged() { - this.element.setAttribute("image", this.image); - this.element.setAttribute("state", - DownloadsCommon.stateOfDownload(this.download)); - - if (this.element.selected) { - goUpdateDownloadCommands(); - } else { - goUpdateCommand("downloadsCmd_clearDownloads"); - } - }, - - onChanged() { - this._updateProgress(); - }, - - /* nsIController */ - isCommandEnabled: function DES_isCommandEnabled(aCommand) { - // The only valid command for inactive elements is cmd_delete. - if (!this.active && aCommand != "cmd_delete") - return false; - switch (aCommand) { - case "downloadsCmd_open": - // This property is false if the download did not succeed. - return this.download.target.exists; - case "downloadsCmd_show": - // TODO: Bug 827010 - Handle part-file asynchronously. - if (this._sessionDownload && this.download.target.partFilePath) { - let partFile = new FileUtils.File(this.download.target.partFilePath); - if (partFile.exists()) { - return true; - } - } - - // This property is false if the download did not succeed. - return this.download.target.exists; - case "downloadsCmd_pauseResume": - return this.download.hasPartialData && !this.download.error; - case "downloadsCmd_retry": - return this.download.canceled || this.download.error; - case "downloadsCmd_openReferrer": - return !!this.download.source.referrer; - case "cmd_delete": - // We don't want in-progress downloads to be removed accidentally. - return this.download.stopped; - case "downloadsCmd_cancel": - return !!this._sessionDownload; - } - return false; - }, - - /* nsIController */ - doCommand: function DES_doCommand(aCommand) { - switch (aCommand) { - case "downloadsCmd_open": { - let file = new FileUtils.File(this.download.target.path); - DownloadsCommon.openDownloadedFile(file, null, window); - break; - } - case "downloadsCmd_show": { - let file = new FileUtils.File(this.download.target.path); - DownloadsCommon.showDownloadedFile(file); - break; - } - case "downloadsCmd_openReferrer": { - openURL(this.download.source.referrer); - break; - } - case "downloadsCmd_cancel": { - this.download.cancel().catch(() => {}); - this.download.removePartialData().catch(Cu.reportError); - break; - } - case "cmd_delete": { - if (this._sessionDownload) { - DownloadsCommon.removeAndFinalizeDownload(this.download); - } - if (this._historyDownload) { - let uri = NetUtil.newURI(this.download.source.url); - PlacesUtils.bhistory.removePage(uri); - } - break; - } - case "downloadsCmd_retry": { - // Errors when retrying are already reported as download failures. - this.download.start().catch(() => {}); - break; - } - case "downloadsCmd_pauseResume": { - // This command is only enabled for session downloads. - if (this.download.stopped) { - this.download.start(); - } else { - this.download.cancel(); - } - break; - } - } - }, - - // Returns whether or not the download handled by this shell should - // show up in the search results for the given term. Both the display - // name for the download and the url are searched. - matchesSearchTerm: function DES_matchesSearchTerm(aTerm) { - if (!aTerm) - return true; - aTerm = aTerm.toLowerCase(); - return this.displayName.toLowerCase().contains(aTerm) || - this.download.source.url.toLowerCase().contains(aTerm); - }, - - // Handles return keypress on the element (the keypress listener is - // set in the DownloadsPlacesView object). - doDefaultCommand: function DES_doDefaultCommand() { - function getDefaultCommandForState(aState) { - switch (aState) { - case nsIDM.DOWNLOAD_FINISHED: - return "downloadsCmd_open"; - case nsIDM.DOWNLOAD_PAUSED: - return "downloadsCmd_pauseResume"; - case nsIDM.DOWNLOAD_NOTSTARTED: - case nsIDM.DOWNLOAD_QUEUED: - return "downloadsCmd_cancel"; - case nsIDM.DOWNLOAD_FAILED: - case nsIDM.DOWNLOAD_CANCELED: - return "downloadsCmd_retry"; - case nsIDM.DOWNLOAD_SCANNING: - return "downloadsCmd_show"; - case nsIDM.DOWNLOAD_BLOCKED_PARENTAL: - case nsIDM.DOWNLOAD_DIRTY: - case nsIDM.DOWNLOAD_BLOCKED_POLICY: - return "downloadsCmd_openReferrer"; - } - return ""; - } - let state = DownloadsCommon.stateOfDownload(this.download); - let command = getDefaultCommandForState(state); - if (command && this.isCommandEnabled(command)) - this.doCommand(command); - }, - - /** - * This method is called by the outer download view, after the controller - * commands have already been updated. In case we did not check for the - * existence of the target file already, we can do it now and then update - * the commands as needed. - */ - onSelect: function DES_onSelect() { - if (!this.active) - return; - - // If this is a history download for which no target file information is - // available, we cannot retrieve information about the target file. - if (!this.download.target.path) { - return; - } - - // Start checking for existence. This may be done twice if onSelect is - // called again before the information is collected. - if (!this._targetFileChecked) { - this._checkTargetFileOnSelect().catch(Cu.reportError); - } - }, - - _checkTargetFileOnSelect: Task.async(function* () { - try { - yield this.download.refresh(); - } finally { - // Do not try to check for existence again if this failed once. - this._targetFileChecked = true; - } - - // Update the commands only if the element is still selected. - if (this.element.selected) { - goUpdateDownloadCommands(); - } - - // Ensure the interface has been updated based on the new values. We need to - // do this because history downloads can't trigger update notifications. - this._updateProgress(); - }), -}; - -/** - * A Downloads Places View is a places view designed to show a places query - * for history downloads alongside the session downloads. - * - * As we don't use the places controller, some methods implemented by other - * places views are not implemented by this view. - * - * A richlistitem in this view can represent either a past download or a session - * download, or both. Session downloads are shown first in the view, and as long - * as they exist they "collapses" their history "counterpart" (So we don't show two - * items for every download). - */ -function DownloadsPlacesView(aRichListBox, aActive = true) { - this._richlistbox = aRichListBox; - this._richlistbox._placesView = this; - window.controllers.insertControllerAt(0, this); - - // Map download URLs to download element shells regardless of their type - this._downloadElementsShellsForURI = new Map(); - - // Map download data items to their element shells. - this._viewItemsForDownloads = new WeakMap(); - - // Points to the last session download element. We keep track of this - // in order to keep all session downloads above past downloads. - this._lastSessionDownloadElement = null; - - this._searchTerm = ""; - - this._active = aActive; - - // Register as a downloads view. The places data will be initialized by - // the places setter. - this._initiallySelectedElement = null; - this._downloadsData = DownloadsCommon.getData(window.opener || window); - this._downloadsData.addView(this); - - // Get the Download button out of the attention state since we're about to - // view all downloads. - DownloadsCommon.getIndicatorData(window).attention = false; - - // Make sure to unregister the view if the window is closed. - window.addEventListener("unload", function() { - window.controllers.removeController(this); - this._downloadsData.removeView(this); - this.result = null; - }.bind(this), true); - // Resizing the window may change items visibility. - window.addEventListener("resize", function() { - this._ensureVisibleElementsAreActive(); - }.bind(this), true); -} - -DownloadsPlacesView.prototype = { - get associatedElement() this._richlistbox, - - get active() this._active, - set active(val) { - this._active = val; - if (this._active) - this._ensureVisibleElementsAreActive(); - return this._active; - }, - - /** - * This cache exists in order to optimize the load of the Downloads View, when - * Places annotations for history downloads must be read. In fact, annotations - * are stored in a single table, and reading all of them at once is much more - * efficient than an individual query. - * - * When this property is first requested, it reads the annotations for all the - * history downloads and stores them indefinitely. - * - * The historical annotations are not expected to change for the duration of - * the session, except in the case where a session download is running for the - * same URI as a history download. To ensure we don't use stale data, URIs - * corresponding to session downloads are permanently removed from the cache. - * This is a very small mumber compared to history downloads. - * - * This property returns a Map from each download source URI found in Places - * annotations to an object with the format: - * - * { targetFileSpec, state, endTime, fileSize, ... } - * - * The targetFileSpec property is the value of "downloads/destinationFileURI", - * while the other properties are taken from "downloads/metaData". Any of the - * properties may be missing from the object. - */ - get _cachedPlacesMetaData() { - if (!this.__cachedPlacesMetaData) { - this.__cachedPlacesMetaData = new Map(); - - // Read the metadata annotations first, but ignore invalid JSON. - for (let result of PlacesUtils.annotations.getAnnotationsWithName( - DOWNLOAD_META_DATA_ANNO)) { - try { - this.__cachedPlacesMetaData.set(result.uri.spec, - JSON.parse(result.annotationValue)); - } catch (ex) {} - } - - // Add the target file annotations to the metadata. - for (let result of PlacesUtils.annotations.getAnnotationsWithName( - DESTINATION_FILE_URI_ANNO)) { - let metaData = this.__cachedPlacesMetaData.get(result.uri.spec); - if (!metaData) { - metaData = {}; - this.__cachedPlacesMetaData.set(result.uri.spec, metaData); - } - metaData.targetFileSpec = result.annotationValue; - } - } - - return this.__cachedPlacesMetaData; - }, - __cachedPlacesMetaData: null, - - /** - * Reads current metadata from Places annotations for the specified URI, and - * returns an object with the format: - * - * { targetFileSpec, state, endTime, fileSize, ... } - * - * The targetFileSpec property is the value of "downloads/destinationFileURI", - * while the other properties are taken from "downloads/metaData". Any of the - * properties may be missing from the object. - */ - _getPlacesMetaDataFor(spec) { - let metaData = {}; - - try { - let uri = NetUtil.newURI(spec); - try { - metaData = JSON.parse(PlacesUtils.annotations.getPageAnnotation( - uri, DOWNLOAD_META_DATA_ANNO)); - } catch (ex) {} - metaData.targetFileSpec = PlacesUtils.annotations.getPageAnnotation( - uri, DESTINATION_FILE_URI_ANNO); - } catch (ex) {} - - return metaData; - }, - - /** - * Given a data item for a session download, or a places node for a past - * download, updates the view as necessary. - * 1. If the given data is a places node, we check whether there are any - * elements for the same download url. If there are, then we just reset - * their places node. Otherwise we add a new download element. - * 2. If the given data is a data item, we first check if there's a history - * download in the list that is not associated with a data item. If we - * found one, we use it for the data item as well and reposition it - * alongside the other session downloads. If we don't, then we go ahead - * and create a new element for the download. - * - * @param [optional] sessionDownload - * A Download object, or null for history downloads. - * @param [optional] aPlacesNode - * The Places node for a history download, or null for session downloads. - * @param [optional] aNewest - * @see onDownloadAdded. Ignored for history downloads. - * @param [optional] aDocumentFragment - * To speed up the appending of multiple elements to the end of the - * list which are coming in a single batch (i.e. invalidateContainer), - * a document fragment may be passed to which the new elements would - * be appended. It's the caller's job to ensure the fragment is merged - * to the richlistbox at the end. - */ - _addDownloadData(sessionDownload, aPlacesNode, aNewest = false, - aDocumentFragment = null) { - let downloadURI = aPlacesNode ? aPlacesNode.uri - : sessionDownload.source.url; - let shellsForURI = this._downloadElementsShellsForURI.get(downloadURI); - if (!shellsForURI) { - shellsForURI = new Set(); - this._downloadElementsShellsForURI.set(downloadURI, shellsForURI); - } - - // When a session download is attached to a shell, we ensure not to keep - // stale metadata around for the corresponding history download. This - // prevents stale state from being used if the view is rebuilt. - // - // Note that we will eagerly load the data in the cache at this point, even - // if we have seen no history download. The case where no history download - // will appear at all is rare enough in normal usage, so we can apply this - // simpler solution rather than keeping a list of cache items to ignore. - if (sessionDownload) { - this._cachedPlacesMetaData.delete(sessionDownload.source.url); - } - - let newOrUpdatedShell = null; - - // Trivial: if there are no shells for this download URI, we always - // need to create one. - let shouldCreateShell = shellsForURI.size == 0; - - // However, if we do have shells for this download uri, there are - // few options: - // 1) There's only one shell and it's for a history download (it has - // no data item). In this case, we update this shell and move it - // if necessary - // 2) There are multiple shells, indicating multiple downloads for - // the same download uri are running. In this case we create - // another shell for the download (so we have one shell for each data - // item). - // - // Note: If a cancelled session download is already in the list, and the - // download is retried, onDownloadAdded is called again for the same - // data item. Thus, we also check that we make sure we don't have a view item - // already. - if (!shouldCreateShell && - sessionDownload && !this._viewItemsForDownloads.has(sessionDownload)) { - // If there's a past-download-only shell for this download-uri with no - // associated data item, use it for the new data item. Otherwise, go ahead - // and create another shell. - shouldCreateShell = true; - for (let shell of shellsForURI) { - if (!shell.sessionDownload) { - shouldCreateShell = false; - shell.sessionDownload = sessionDownload; - newOrUpdatedShell = shell; - this._viewItemsForDownloads.set(sessionDownload, shell); - break; - } - } - } - - if (shouldCreateShell) { - // If we are adding a new history download here, it means there is no - // associated session download, thus we must read the Places metadata, - // because it will not be obscured by the session download. - let historyDownload = null; - if (aPlacesNode) { - let metaData = this._cachedPlacesMetaData.get(aPlacesNode.uri) || - this._getPlacesMetaDataFor(aPlacesNode.uri); - historyDownload = new HistoryDownload(aPlacesNode); - historyDownload.updateFromMetaData(metaData); - } - let shell = new HistoryDownloadElementShell(sessionDownload, - historyDownload); - shell.element._placesNode = aPlacesNode; - newOrUpdatedShell = shell; - shellsForURI.add(shell); - if (sessionDownload) { - this._viewItemsForDownloads.set(sessionDownload, shell); - } - } - else if (aPlacesNode) { - // We are updating information for a history download for which we have - // at least one download element shell already. There are two cases: - // 1) There are one or more download element shells for this source URI, - // each with an associated session download. We update the Places node - // because we may need it later, but we don't need to read the Places - // metadata until the last session download is removed. - // 2) Occasionally, we may receive a duplicate notification for a history - // download with no associated session download. We have exactly one - // download element shell in this case, but the metdata cannot have - // changed, just the reference to the Places node object is different. - // So, we update all the node references and keep the metadata intact. - for (let shell of shellsForURI) { - if (!shell.historyDownload) { - // Create the element to host the metadata when needed. - shell.historyDownload = new HistoryDownload(aPlacesNode); - } - shell.element._placesNode = aPlacesNode; - } - } - - if (newOrUpdatedShell) { - if (aNewest) { - this._richlistbox.insertBefore(newOrUpdatedShell.element, - this._richlistbox.firstChild); - if (!this._lastSessionDownloadElement) { - this._lastSessionDownloadElement = newOrUpdatedShell.element; - } - // Some operations like retrying an history download move an element to - // the top of the richlistbox, along with other session downloads. - // More generally, if a new download is added, should be made visible. - this._richlistbox.ensureElementIsVisible(newOrUpdatedShell.element); - } else if (sessionDownload) { - let before = this._lastSessionDownloadElement ? - this._lastSessionDownloadElement.nextSibling : this._richlistbox.firstChild; - this._richlistbox.insertBefore(newOrUpdatedShell.element, before); - this._lastSessionDownloadElement = newOrUpdatedShell.element; - } - else { - let appendTo = aDocumentFragment || this._richlistbox; - appendTo.appendChild(newOrUpdatedShell.element); - } - - if (this.searchTerm) { - newOrUpdatedShell.element.hidden = - !newOrUpdatedShell.element._shell.matchesSearchTerm(this.searchTerm); - } - } - - // If aDocumentFragment is defined this is a batch change, so it's up to - // the caller to append the fragment and activate the visible shells. - if (!aDocumentFragment) { - this._ensureVisibleElementsAreActive(); - goUpdateCommand("downloadsCmd_clearDownloads"); - } - }, - - _removeElement: function DPV__removeElement(aElement) { - // If the element was selected exclusively, select its next - // sibling first, if not, try for previous sibling, if any. - if ((aElement.nextSibling || aElement.previousSibling) && - this._richlistbox.selectedItems && - this._richlistbox.selectedItems.length == 1 && - this._richlistbox.selectedItems[0] == aElement) { - this._richlistbox.selectItem(aElement.nextSibling || - aElement.previousSibling); - } - - if (this._lastSessionDownloadElement == aElement) - this._lastSessionDownloadElement = aElement.previousSibling; - - this._richlistbox.removeItemFromSelection(aElement); - this._richlistbox.removeChild(aElement); - this._ensureVisibleElementsAreActive(); - goUpdateCommand("downloadsCmd_clearDownloads"); - }, - - _removeHistoryDownloadFromView: - function DPV__removeHistoryDownloadFromView(aPlacesNode) { - let downloadURI = aPlacesNode.uri; - let shellsForURI = this._downloadElementsShellsForURI.get(downloadURI); - if (shellsForURI) { - for (let shell of shellsForURI) { - if (shell.sessionDownload) { - shell.historyDownload = null; - } - else { - this._removeElement(shell.element); - shellsForURI.delete(shell); - if (shellsForURI.size == 0) - this._downloadElementsShellsForURI.delete(downloadURI); - } - } - } - }, - - _removeSessionDownloadFromView(download) { - let shells = this._downloadElementsShellsForURI - .get(download.source.url); - if (shells.size == 0) - throw new Error("Should have had at leaat one shell for this uri"); - - let shell = this._viewItemsForDownloads.get(download); - if (!shells.has(shell)) - throw new Error("Missing download element shell in shells list for url"); - - // If there's more than one item for this download uri, we can let the - // view item for this this particular data item go away. - // If there's only one item for this download uri, we should only - // keep it if it is associated with a history download. - if (shells.size > 1 || !shell.historyDownload) { - this._removeElement(shell.element); - shells.delete(shell); - if (shells.size == 0) - this._downloadElementsShellsForURI.delete(download.source.url); - } - else { - // We have one download element shell containing both a session download - // and a history download, and we are now removing the session download. - // Previously, we did not use the Places metadata because it was obscured - // by the session download. Since this is no longer the case, we have to - // read the latest metadata before removing the session download. - let url = shell.historyDownload.source.url; - let metaData = this._getPlacesMetaDataFor(url); - shell.historyDownload.updateFromMetaData(metaData); - shell.sessionDownload = null; - // Move it below the session-download items; - if (this._lastSessionDownloadElement == shell.element) { - this._lastSessionDownloadElement = shell.element.previousSibling; - } - else { - let before = this._lastSessionDownloadElement ? - this._lastSessionDownloadElement.nextSibling : this._richlistbox.firstChild; - this._richlistbox.insertBefore(shell.element, before); - } - } - }, - - _ensureVisibleElementsAreActive: - function DPV__ensureVisibleElementsAreActive() { - if (!this.active || this._ensureVisibleTimer || !this._richlistbox.firstChild) - return; - - this._ensureVisibleTimer = setTimeout(function() { - delete this._ensureVisibleTimer; - if (!this._richlistbox.firstChild) - return; - - let rlbRect = this._richlistbox.getBoundingClientRect(); - let winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - let nodes = winUtils.nodesFromRect(rlbRect.left, rlbRect.top, - 0, rlbRect.width, rlbRect.height, 0, - true, false); - // nodesFromRect returns nodes in z-index order, and for the same z-index - // sorts them in inverted DOM order, thus starting from the one that would - // be on top. - let firstVisibleNode, lastVisibleNode; - for (let node of nodes) { - if (node.localName === "richlistitem" && node._shell) { - node._shell.ensureActive(); - // The first visible node is the last match. - firstVisibleNode = node; - // While the last visible node is the first match. - if (!lastVisibleNode) - lastVisibleNode = node; - } - } - - // Also activate the first invisible nodes in both boundaries (that is, - // above and below the visible area) to ensure proper keyboard navigation - // in both directions. - let nodeBelowVisibleArea = lastVisibleNode && lastVisibleNode.nextSibling; - if (nodeBelowVisibleArea && nodeBelowVisibleArea._shell) - nodeBelowVisibleArea._shell.ensureActive(); - - let nodeABoveVisibleArea = - firstVisibleNode && firstVisibleNode.previousSibling; - if (nodeABoveVisibleArea && nodeABoveVisibleArea._shell) - nodeABoveVisibleArea._shell.ensureActive(); - }.bind(this), 10); - }, - - _place: "", - get place() this._place, - set place(val) { - // Don't reload everything if we don't have to. - if (this._place == val) { - // XXXmano: places.js relies on this behavior (see Bug 822203). - this.searchTerm = ""; - return val; - } - - this._place = val; - - let history = PlacesUtils.history; - let queries = { }, options = { }; - history.queryStringToQueries(val, queries, { }, options); - if (!queries.value.length) - queries.value = [history.getNewQuery()]; - - let result = history.executeQueries(queries.value, queries.value.length, - options.value); - result.addObserver(this, false); - return val; - }, - - _result: null, - get result() this._result, - set result(val) { - if (this._result == val) - return val; - - if (this._result) { - this._result.removeObserver(this); - this._resultNode.containerOpen = false; - } - - if (val) { - this._result = val; - this._resultNode = val.root; - this._resultNode.containerOpen = true; - this._ensureInitialSelection(); - } - else { - delete this._resultNode; - delete this._result; - } - - return val; - }, - - get selectedNodes() { - return [for (element of this._richlistbox.selectedItems) - if (element._placesNode) - element._placesNode]; - }, - - get selectedNode() { - let selectedNodes = this.selectedNodes; - return selectedNodes.length == 1 ? selectedNodes[0] : null; - }, - - get hasSelection() this.selectedNodes.length > 0, - - containerStateChanged: - function DPV_containerStateChanged(aNode, aOldState, aNewState) { - this.invalidateContainer(aNode) - }, - - invalidateContainer: - function DPV_invalidateContainer(aContainer) { - if (aContainer != this._resultNode) - throw new Error("Unexpected container node"); - if (!aContainer.containerOpen) - throw new Error("Root container for the downloads query cannot be closed"); - - let suppressOnSelect = this._richlistbox.suppressOnSelect; - this._richlistbox.suppressOnSelect = true; - try { - // Remove the invalidated history downloads from the list and unset the - // places node for data downloads. - // Loop backwards since _removeHistoryDownloadFromView may removeChild(). - for (let i = this._richlistbox.childNodes.length - 1; i >= 0; --i) { - let element = this._richlistbox.childNodes[i]; - if (element._placesNode) { - this._removeHistoryDownloadFromView(element._placesNode); - } - } - } - finally { - this._richlistbox.suppressOnSelect = suppressOnSelect; - } - - if (aContainer.childCount > 0) { - let elementsToAppendFragment = document.createDocumentFragment(); - for (let i = 0; i < aContainer.childCount; i++) { - try { - this._addDownloadData(null, aContainer.getChild(i), false, - elementsToAppendFragment); - } - catch(ex) { - Cu.reportError(ex); - } - } - - // _addDownloadData may not add new elements if there were already - // data items in place. - if (elementsToAppendFragment.firstChild) { - this._appendDownloadsFragment(elementsToAppendFragment); - this._ensureVisibleElementsAreActive(); - } - } - - goUpdateDownloadCommands(); - }, - - _appendDownloadsFragment: function DPV__appendDownloadsFragment(aDOMFragment) { - // Workaround multiple reflows hang by removing the richlistbox - // and adding it back when we're done. - - // Hack for bug 836283: reset xbl fields to their old values after the - // binding is reattached to avoid breaking the selection state - let xblFields = new Map(); - for (let [key, value] in Iterator(this._richlistbox)) { - xblFields.set(key, value); - } - - let parentNode = this._richlistbox.parentNode; - let nextSibling = this._richlistbox.nextSibling; - parentNode.removeChild(this._richlistbox); - this._richlistbox.appendChild(aDOMFragment); - parentNode.insertBefore(this._richlistbox, nextSibling); - - for (let [key, value] of xblFields) { - this._richlistbox[key] = value; - } - }, - - nodeInserted: function DPV_nodeInserted(aParent, aPlacesNode) { - this._addDownloadData(null, aPlacesNode); - }, - - nodeRemoved: function DPV_nodeRemoved(aParent, aPlacesNode, aOldIndex) { - this._removeHistoryDownloadFromView(aPlacesNode); - }, - - nodeAnnotationChanged() {}, - nodeIconChanged() {}, - nodeTitleChanged() {}, - nodeKeywordChanged: function() {}, - nodeDateAddedChanged: function() {}, - nodeLastModifiedChanged: function() {}, - nodeHistoryDetailsChanged: function() {}, - nodeTagsChanged: function() {}, - sortingChanged: function() {}, - nodeMoved: function() {}, - nodeURIChanged: function() {}, - batching: function() {}, - - get controller() this._richlistbox.controller, - - get searchTerm() this._searchTerm, - set searchTerm(aValue) { - if (this._searchTerm != aValue) { - for (let element of this._richlistbox.childNodes) { - element.hidden = !element._shell.matchesSearchTerm(aValue); - } - this._ensureVisibleElementsAreActive(); - } - return this._searchTerm = aValue; - }, - - /** - * When the view loads, we want to select the first item. - * However, because session downloads, for which the data is loaded - * asynchronously, always come first in the list, and because the list - * may (or may not) already contain history downloads at that point, it - * turns out that by the time we can select the first item, the user may - * have already started using the view. - * To make things even more complicated, in other cases, the places data - * may be loaded after the session downloads data. Thus we cannot rely on - * the order in which the data comes in. - * We work around this by attempting to select the first element twice, - * once after the places data is loaded and once when the session downloads - * data is done loading. However, if the selection has changed in-between, - * we assume the user has already started using the view and give up. - */ - _ensureInitialSelection: function DPV__ensureInitialSelection() { - // Either they're both null, or the selection has not changed in between. - if (this._richlistbox.selectedItem == this._initiallySelectedElement) { - let firstDownloadElement = this._richlistbox.firstChild; - if (firstDownloadElement != this._initiallySelectedElement) { - // We may be called before _ensureVisibleElementsAreActive, - // or before the download binding is attached. Therefore, ensure the - // first item is activated, and pass the item to the richlistbox - // setters only at a point we know for sure the binding is attached. - firstDownloadElement._shell.ensureActive(); - Services.tm.mainThread.dispatch(function() { - this._richlistbox.selectedItem = firstDownloadElement; - this._richlistbox.currentItem = firstDownloadElement; - this._initiallySelectedElement = firstDownloadElement; - }.bind(this), Ci.nsIThread.DISPATCH_NORMAL); - } - } - }, - - onDataLoadStarting: function() { }, - onDataLoadCompleted: function DPV_onDataLoadCompleted() { - this._ensureInitialSelection(); - }, - - onDownloadAdded(download, newest) { - this._addDownloadData(download, null, newest); - }, - - onDownloadStateChanged(download) { - this._viewItemsForDownloads.get(download).onStateChanged(); - }, - - onDownloadChanged(download) { - this._viewItemsForDownloads.get(download).onChanged(); - }, - - onDownloadRemoved(download) { - this._removeSessionDownloadFromView(download); - }, - - supportsCommand: function DPV_supportsCommand(aCommand) { - if (DOWNLOAD_VIEW_SUPPORTED_COMMANDS.indexOf(aCommand) != -1) { - // The clear-downloads command may be performed by the toolbar-button, - // which can be focused on OS X. Thus enable this command even if the - // richlistbox is not focused. - // For other commands, be prudent and disable them unless the richlistview - // is focused. It's important to make the decision here rather than in - // isCommandEnabled. Otherwise our controller may "steal" commands from - // other controls in the window (see goUpdateCommand & - // getControllerForCommand). - if (document.activeElement == this._richlistbox || - aCommand == "downloadsCmd_clearDownloads") { - return true; - } - } - return false; - }, - - isCommandEnabled: function DPV_isCommandEnabled(aCommand) { - switch (aCommand) { - case "cmd_copy": - return this._richlistbox.selectedItems.length > 0; - case "cmd_selectAll": - return true; - case "cmd_paste": - return this._canDownloadClipboardURL(); - case "downloadsCmd_clearDownloads": - return this._canClearDownloads(); - default: - return Array.every(this._richlistbox.selectedItems, function(element) { - return element._shell.isCommandEnabled(aCommand); - }); - } - }, - - _canClearDownloads: function DPV__canClearDownloads() { - // Downloads can be cleared if there's at least one removable download in - // the list (either a history download or a completed session download). - // Because history downloads are always removable and are listed after the - // session downloads, check from bottom to top. - for (let elt = this._richlistbox.lastChild; elt; elt = elt.previousSibling) { - // Stopped, paused, and failed downloads with partial data are removed. - let download = elt._shell.download; - if (download.stopped && !(download.canceled && download.hasPartialData)) { - return true; - } - } - return false; - }, - - _copySelectedDownloadsToClipboard: - function DPV__copySelectedDownloadsToClipboard() { - let urls = [for (element of this._richlistbox.selectedItems) - element._shell.download.source.url]; - - Cc["@mozilla.org/widget/clipboardhelper;1"] - .getService(Ci.nsIClipboardHelper) - .copyString(urls.join("\n"), document); - }, - - _getURLFromClipboardData: function DPV__getURLFromClipboardData() { - let trans = Cc["@mozilla.org/widget/transferable;1"]. - createInstance(Ci.nsITransferable); - trans.init(null); - - let flavors = ["text/x-moz-url", "text/unicode"]; - flavors.forEach(trans.addDataFlavor); - - Services.clipboard.getData(trans, Services.clipboard.kGlobalClipboard); - - // Getting the data or creating the nsIURI might fail. - try { - let data = {}; - trans.getAnyTransferData({}, data, {}); - let [url, name] = data.value.QueryInterface(Ci.nsISupportsString) - .data.split("\n"); - if (url) - return [NetUtil.newURI(url, null, null).spec, name]; - } - catch(ex) { } - - return ["", ""]; - }, - - _canDownloadClipboardURL: function DPV__canDownloadClipboardURL() { - let [url, name] = this._getURLFromClipboardData(); - return url != ""; - }, - - _downloadURLFromClipboard: function DPV__downloadURLFromClipboard() { - let [url, name] = this._getURLFromClipboardData(); - let browserWin = RecentWindow.getMostRecentBrowserWindow(); - let initiatingDoc = browserWin ? browserWin.document : document; - DownloadURL(url, name, initiatingDoc); - }, - - doCommand: function DPV_doCommand(aCommand) { - // Commands may be invoked with keyboard shortcuts even if disabled. - if (!this.isCommandEnabled(aCommand)) { - return; - } - switch (aCommand) { - case "cmd_copy": - this._copySelectedDownloadsToClipboard(); - break; - case "cmd_selectAll": - this._richlistbox.selectAll(); - break; - case "cmd_paste": - this._downloadURLFromClipboard(); - break; - case "downloadsCmd_clearDownloads": - this._downloadsData.removeFinished(); - if (this.result) { - Cc["@mozilla.org/browser/download-history;1"] - .getService(Ci.nsIDownloadHistory) - .removeAllDownloads(); - } - // There may be no selection or focus change as a result - // of these change, and we want the command updated immediately. - goUpdateCommand("downloadsCmd_clearDownloads"); - break; - default: { - // Cloning the nodelist into an array to get a frozen list of selected items. - // Otherwise, the selectedItems nodelist is live and doCommand may alter the - // selection while we are trying to do one particular action, like removing - // items from history. - let selectedElements = [...this._richlistbox.selectedItems]; - for (let element of selectedElements) { - element._shell.doCommand(aCommand); - } - } - } - }, - - onEvent: function() { }, - - onContextMenu: function DPV_onContextMenu(aEvent) - { - let element = this._richlistbox.selectedItem; - if (!element || !element._shell) - return false; - - // Set the state attribute so that only the appropriate items are displayed. - let contextMenu = document.getElementById("downloadsContextMenu"); - let download = element._shell.download; - contextMenu.setAttribute("state", - DownloadsCommon.stateOfDownload(download)); - - if (!download.stopped) { - // The hasPartialData property of a download may change at any time after - // it has started, so ensure we update the related command now. - goUpdateCommand("downloadsCmd_pauseResume"); - } - return true; - }, - - onKeyPress: function DPV_onKeyPress(aEvent) { - let selectedElements = this._richlistbox.selectedItems; - if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN) { - // In the content tree, opening bookmarks by pressing return is only - // supported when a single item is selected. To be consistent, do the - // same here. - if (selectedElements.length == 1) { - let element = selectedElements[0]; - if (element._shell) - element._shell.doDefaultCommand(); - } - } - else if (aEvent.charCode == " ".charCodeAt(0)) { - // Pause/Resume every selected download - for (let element of selectedElements) { - if (element._shell.isCommandEnabled("downloadsCmd_pauseResume")) - element._shell.doCommand("downloadsCmd_pauseResume"); - } - } - }, - - onDoubleClick: function DPV_onDoubleClick(aEvent) { - if (aEvent.button != 0) - return; - - let selectedElements = this._richlistbox.selectedItems; - if (selectedElements.length != 1) - return; - - let element = selectedElements[0]; - if (element._shell) - element._shell.doDefaultCommand(); - }, - - onScroll: function DPV_onScroll() { - this._ensureVisibleElementsAreActive(); - }, - - onSelect: function DPV_onSelect() { - goUpdateDownloadCommands(); - - let selectedElements = this._richlistbox.selectedItems; - for (let elt of selectedElements) { - if (elt._shell) - elt._shell.onSelect(); - } - }, - - onDragStart: function DPV_onDragStart(aEvent) { - // TODO Bug 831358: Support d&d for multiple selection. - // For now, we just drag the first element. - let selectedItem = this._richlistbox.selectedItem; - if (!selectedItem) - return; - - let targetPath = selectedItem._shell.download.target.path; - if (!targetPath) { - return; - } - - // We must check for existence synchronously because this is a DOM event. - let file = new FileUtils.File(targetPath); - if (!file.exists()) - return; - - let dt = aEvent.dataTransfer; - dt.mozSetDataAt("application/x-moz-file", file, 0); - let url = Services.io.newFileURI(file).spec; - dt.setData("text/uri-list", url); - dt.setData("text/plain", url); - dt.effectAllowed = "copyMove"; - dt.addElement(selectedItem); - }, - - onDragOver: function DPV_onDragOver(aEvent) { - let types = aEvent.dataTransfer.types; - if (types.contains("text/uri-list") || - types.contains("text/x-moz-url") || - types.contains("text/plain")) { - aEvent.preventDefault(); - } - }, - - onDrop: function DPV_onDrop(aEvent) { - let dt = aEvent.dataTransfer; - // If dragged item is from our source, do not try to - // redownload already downloaded file. - if (dt.mozGetDataAt("application/x-moz-file", 0)) - return; - - let links = Services.droppedLinkHandler.dropLinks(aEvent); - if (!links.length) - return; - let browserWin = RecentWindow.getMostRecentBrowserWindow(); - let initiatingDoc = browserWin ? browserWin.document : document; - for (let link of links) { - if (link.url.startsWith("about:")) - continue; - DownloadURL(link.url, link.name, initiatingDoc); - } - } -}; - -for (let methodName of ["load", "applyFilter", "selectNode", "selectItems"]) { - DownloadsPlacesView.prototype[methodName] = function() { - throw new Error("|" + methodName + "| is not implemented by the downloads view."); - } -} - -function goUpdateDownloadCommands() { - for (let command of DOWNLOAD_VIEW_SUPPORTED_COMMANDS) { - goUpdateCommand(command); - } -} diff --git a/components/downloads/content/allDownloadsViewOverlay.xul b/components/downloads/content/allDownloadsViewOverlay.xul deleted file mode 100644 index 4e9bfd1..0000000 --- a/components/downloads/content/allDownloadsViewOverlay.xul +++ /dev/null @@ -1,119 +0,0 @@ -<?xml version="1.0"?> - -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -<?xml-stylesheet href="chrome://browser/content/downloads/allDownloadsViewOverlay.css"?> -<?xml-stylesheet href="chrome://browser/skin/downloads/allDownloadsViewOverlay.css"?> - -<!DOCTYPE overlay [ -<!ENTITY % downloadsDTD SYSTEM "chrome://browser/locale/downloads/downloads.dtd"> -%downloadsDTD; -]> - -<!-- This overlay provides a downloads view that lists both session downloads, - using the DownloadsView API, and history downloads, using places queries. - The view also implements a command controller and a context menu for - managing the downloads list. In order to use this view: - 1. Apply this overlay to your window. - 2. Insert in all the overlay entry-points, namely: - <richlistbox id="downloadsRichListBox"/> - <commandset id="downloadCommands"/> - <menupopup id="downloadsContextMenu"/> - 3. Make sure your window has the editMenuOverlay overlay applied, - because the view implements cmd_copy and cmd_delete. - 4. Make sure your window has the globalOverlay.js script loaded. - 5. To initialize the view - let view = new DownloadsPlacesView(document.getElementById("downloadsRichListBox")); - // This is what the Places Library uses. It could be tweaked a bit as long as the - // transition-type is set correctly - view.place = "place:transition=7&sort=4"; ---> -<overlay id="downloadsViewOverlay" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - - <script type="application/javascript" - src="chrome://browser/content/downloads/allDownloadsViewOverlay.js"/> - <script type="application/javascript" - src="chrome://global/content/contentAreaUtils.js"/> - - <richlistbox flex="1" - seltype="multiple" - id="downloadsRichListBox" context="downloadsContextMenu" - onscroll="return this._placesView.onScroll();" - onkeypress="return this._placesView.onKeyPress(event);" - ondblclick="return this._placesView.onDoubleClick(event);" - oncontextmenu="return this._placesView.onContextMenu(event);" - ondragstart="this._placesView.onDragStart(event);" - ondragover="this._placesView.onDragOver(event);" - ondrop="this._placesView.onDrop(event);" - onfocus="goUpdateDownloadCommands();" - onselect="this._placesView.onSelect();" - onblur="goUpdateDownloadCommands();"/> - - <commandset id="downloadCommands" - commandupdater="true" - events="focus,select,contextmenu" - oncommandupdate="goUpdateDownloadCommands();"> - <command id="downloadsCmd_pauseResume" - oncommand="goDoCommand('downloadsCmd_pauseResume')"/> - <command id="downloadsCmd_cancel" - oncommand="goDoCommand('downloadsCmd_cancel')"/> - <command id="downloadsCmd_open" - oncommand="goDoCommand('downloadsCmd_open')"/> - <command id="downloadsCmd_show" - oncommand="goDoCommand('downloadsCmd_show')"/> - <command id="downloadsCmd_retry" - oncommand="goDoCommand('downloadsCmd_retry')"/> - <command id="downloadsCmd_openReferrer" - oncommand="goDoCommand('downloadsCmd_openReferrer')"/> - <command id="downloadsCmd_clearDownloads" - oncommand="goDoCommand('downloadsCmd_clearDownloads')"/> - </commandset> - - <menupopup id="downloadsContextMenu" class="download-state"> - <menuitem command="downloadsCmd_pauseResume" - class="downloadPauseMenuItem" - label="&cmd.pause.label;" - accesskey="&cmd.pause.accesskey;"/> - <menuitem command="downloadsCmd_pauseResume" - class="downloadResumeMenuItem" - label="&cmd.resume.label;" - accesskey="&cmd.resume.accesskey;"/> - <menuitem command="downloadsCmd_cancel" - class="downloadCancelMenuItem" - label="&cmd.cancel.label;" - accesskey="&cmd.cancel.accesskey;"/> - <menuitem command="cmd_delete" - class="downloadRemoveFromHistoryMenuItem" - label="&cmd.removeFromHistory.label;" - accesskey="&cmd.removeFromHistory.accesskey;"/> - <menuitem command="downloadsCmd_show" - class="downloadShowMenuItem" -#ifdef XP_MACOSX - label="&cmd.showMac.label;" - accesskey="&cmd.showMac.accesskey;" -#else - label="&cmd.show.label;" - accesskey="&cmd.show.accesskey;" -#endif - /> - - <menuseparator class="downloadCommandsSeparator"/> - - <menuitem command="downloadsCmd_openReferrer" - label="&cmd.goToDownloadPage.label;" - accesskey="&cmd.goToDownloadPage.accesskey;"/> - <menuitem command="cmd_copy" - label="&cmd.copyDownloadLink.label;" - accesskey="&cmd.copyDownloadLink.accesskey;"/> - - <menuseparator/> - - <menuitem command="downloadsCmd_clearDownloads" - label="&cmd.clearDownloads.label;" - accesskey="&cmd.clearDownloads.accesskey;"/> - </menupopup> -</overlay> diff --git a/components/downloads/content/contentAreaDownloadsView.css b/components/downloads/content/contentAreaDownloadsView.css deleted file mode 100644 index abaae1f..0000000 --- a/components/downloads/content/contentAreaDownloadsView.css +++ /dev/null @@ -1,11 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#downloadsListEmptyDescription { - display: none; -} - -#downloadsRichListBox:empty + #downloadsListEmptyDescription { - display: -moz-box; -} diff --git a/components/downloads/content/contentAreaDownloadsView.js b/components/downloads/content/contentAreaDownloadsView.js deleted file mode 100644 index fbb18ab..0000000 --- a/components/downloads/content/contentAreaDownloadsView.js +++ /dev/null @@ -1,15 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); - -var ContentAreaDownloadsView = { - init: function CADV_init() { - let view = new DownloadsPlacesView(document.getElementById("downloadsRichListBox")); - // Do not display the Places downloads in private windows - if (!PrivateBrowsingUtils.isWindowPrivate(window)) { - view.place = "place:transition=7&sort=4"; - } - } -}; diff --git a/components/downloads/content/contentAreaDownloadsView.xul b/components/downloads/content/contentAreaDownloadsView.xul deleted file mode 100644 index a91de1e..0000000 --- a/components/downloads/content/contentAreaDownloadsView.xul +++ /dev/null @@ -1,45 +0,0 @@ -<?xml version="1.0"?> - -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -<?xml-stylesheet href="chrome://global/skin/"?> -<?xml-stylesheet href="chrome://browser/content/downloads/contentAreaDownloadsView.css"?> -<?xml-stylesheet href="chrome://browser/skin/downloads/contentAreaDownloadsView.css"?> - -<?xul-overlay href="chrome://browser/content/downloads/allDownloadsViewOverlay.xul"?> - -<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?> - -<!DOCTYPE window [ -<!ENTITY % downloadsDTD SYSTEM "chrome://browser/locale/downloads/downloads.dtd"> -%downloadsDTD; -]> - -<window id="contentAreaDownloadsView" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - title="&downloads.title;" - onload="ContentAreaDownloadsView.init();"> - - <script type="application/javascript" - src="chrome://global/content/globalOverlay.js"/> - <script type="application/javascript" - src="chrome://browser/content/downloads/contentAreaDownloadsView.js"/> - - <commandset id="editMenuCommands"/> - - <keyset id="editMenuKeys"> -#ifdef XP_MACOSX - <key id="key_delete2" keycode="VK_BACK" command="cmd_delete"/> -#endif - </keyset> - - <stack flex="1"> - <richlistbox id="downloadsRichListBox"/> - <description id="downloadsListEmptyDescription" - value="&downloadsListEmpty.label;"/> - </stack> - <commandset id="downloadCommands"/> - <menupopup id="downloadsContextMenu"/> -</window> diff --git a/components/downloads/content/download.css b/components/downloads/content/download.css deleted file mode 100644 index 7412fa7..0000000 --- a/components/downloads/content/download.css +++ /dev/null @@ -1,45 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -richlistitem.download button { - /* These buttons should never get focus, as that would "disable" - the downloads view controller (it's only used when the richlistbox - is focused). */ - -moz-user-focus: none; -} - -/*** Visibility of controls inside download items ***/ - -.download-state:-moz-any( [state="6"], /* Blocked (parental) */ - [state="8"], /* Blocked (dirty) */ - [state="9"]) /* Blocked (policy) */ - > .downloadTypeIcon:not(.blockedIcon), - -.download-state:not(:-moz-any([state="6"], /* Blocked (parental) */ - [state="8"], /* Blocked (dirty) */ - [state="9"]) /* Blocked (policy) */) - > .downloadTypeIcon.blockedIcon, - -.download-state:not(:-moz-any([state="-1"],/* Starting (initial) */ - [state="5"], /* Starting (queued) */ - [state="0"], /* Downloading */ - [state="4"], /* Paused */ - [state="7"]) /* Scanning */) - > vbox > .downloadProgress, - -.download-state:not(:-moz-any([state="-1"],/* Starting (initial) */ - [state="5"], /* Starting (queued) */ - [state="0"], /* Downloading */ - [state="4"]) /* Paused */) - > .downloadCancel, - -.download-state[state]:not(:-moz-any([state="2"], /* Failed */ - [state="3"]) /* Canceled */) - > .downloadRetry, - -.download-state:not( [state="1"] /* Finished */) - > .downloadShow -{ - display: none; -} diff --git a/components/downloads/content/download.xml b/components/downloads/content/download.xml deleted file mode 100644 index 542901b..0000000 --- a/components/downloads/content/download.xml +++ /dev/null @@ -1,188 +0,0 @@ -<?xml version="1.0"?> -<!-- -*- Mode: HTML; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- --> -<!-- vim: set ts=2 et sw=2 tw=80: --> - -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this file, - - You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<!DOCTYPE bindings SYSTEM "chrome://browser/locale/downloads/downloads.dtd"> - -<bindings id="downloadBindings" - xmlns="http://www.mozilla.org/xbl" - xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:xbl="http://www.mozilla.org/xbl"> - - <binding id="download" - extends="chrome://global/content/bindings/richlistbox.xml#richlistitem"> - <content orient="horizontal" - align="center" - onclick="DownloadsView.onDownloadClick(event);"> - <xul:image class="downloadTypeIcon" - validate="always" - xbl:inherits="src=image"/> - <xul:image class="downloadTypeIcon blockedIcon"/> - <xul:vbox pack="center" - flex="1" - class="downloadContainer" - style="width: &downloadDetails.width;"> - <!-- We're letting localizers put a min-width in here primarily - because of the downloads summary at the bottom of the list of - download items. An element in the summary has the same min-width - on a description, and we don't want the panel to change size if the - summary isn't being displayed, so we ensure that items share the - same minimum width. - --> - <xul:description class="downloadDisplayName" - crop="center" - style="min-width: &downloadsSummary.minWidth2;" - xbl:inherits="value=displayName,tooltiptext=displayName"/> - <xul:progressmeter anonid="progressmeter" - class="downloadProgress" - min="0" - max="100" - xbl:inherits="mode=progressmode,value=progress"/> - <xul:description class="downloadDetails" - crop="end" - xbl:inherits="value=status,tooltiptext=statusTip"/> - </xul:vbox> - <xul:stack> - <xul:button class="downloadButton downloadCancel" - tooltiptext="&cmd.cancel.label;" - oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_cancel');"/> - <xul:button class="downloadButton downloadRetry" - tooltiptext="&cmd.retry.label;" - oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_retry');"/> - <xul:button class="downloadButton downloadShow" -#ifdef XP_MACOSX - tooltiptext="&cmd.showMac.label;" -#else - tooltiptext="&cmd.show.label;" -#endif - oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_show');"/> - </xul:stack> - </content> - </binding> - - <binding id="download-in-progress" - extends="chrome://global/content/bindings/richlistbox.xml#richlistitem"> - <content orient="horizontal" - align="center" - onclick="DownloadsView.onDownloadClick(event);"> - <xul:image class="downloadTypeIcon" - validate="always" - xbl:inherits="src=image"/> - <xul:image class="downloadTypeIcon blockedIcon"/> - <xul:vbox pack="center" - flex="1" - class="downloadContainer" - style="width: &downloadDetails.width;"> - <xul:description class="downloadDisplayName" - crop="center" - style="min-width: &downloadsSummary.minWidth2;" - xbl:inherits="value=displayName,tooltiptext=extendedDisplayNameTip"/> - <xul:progressmeter anonid="progressmeter" - class="downloadProgress" - min="0" - max="100" - xbl:inherits="mode=progressmode,value=progress"/> - <xul:description class="downloadDetails" - crop="end" - xbl:inherits="value=status,tooltiptext=statusTip"/> - </xul:vbox> - <xul:stack> - <xul:button class="downloadButton downloadCancel" - tooltiptext="&cmd.cancel.label;" - oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_cancel');"/> - <xul:button class="downloadButton downloadRetry" - tooltiptext="&cmd.retry.label;" - oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_retry');"/> - <xul:button class="downloadButton downloadShow" - tooltiptext="&cmd.show.label;" - oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_show');"/> - </xul:stack> - </content> - </binding> - - <binding id="download-full-ui" - extends="chrome://global/content/bindings/richlistbox.xml#richlistitem"> - <resources> - <stylesheet src="chrome://browser/content/downloads/download.css"/> - </resources> - - <content orient="horizontal" align="center"> - <xul:image class="downloadTypeIcon" - validate="always" - xbl:inherits="src=image"/> - <xul:image class="downloadTypeIcon blockedIcon"/> - <xul:vbox pack="center" flex="1"> - <xul:description class="downloadDisplayName" - crop="center" - xbl:inherits="value=displayName,tooltiptext=displayName"/> - <xul:progressmeter anonid="progressmeter" - class="downloadProgress" - min="0" - max="100" - xbl:inherits="mode=progressmode,value=progress"/> - <xul:description class="downloadDetails" - style="width: &downloadDetails.width;" - crop="end" - xbl:inherits="value=status,tooltiptext=statusTip"/> - </xul:vbox> - - <xul:button class="downloadButton downloadCancel" - tooltiptext="&cmd.cancel.label;" - oncommand="goDoCommand('downloadsCmd_cancel')"/> - <xul:button class="downloadButton downloadRetry" - tooltiptext="&cmd.retry.label;" - oncommand="goDoCommand('downloadsCmd_retry')"/> - <xul:button class="downloadButton downloadShow" -#ifdef XP_MACOSX - tooltiptext="&cmd.showMac.label;" -#else - tooltiptext="&cmd.show.label;" -#endif - oncommand="goDoCommand('downloadsCmd_show')"/> - - </content> - </binding> - - <binding id="download-in-progress-full-ui" - extends="chrome://global/content/bindings/richlistbox.xml#richlistitem"> - <resources> - <stylesheet src="chrome://browser/content/downloads/download.css"/> - </resources> - - <content orient="horizontal" align="center"> - <xul:image class="downloadTypeIcon" - validate="always" - xbl:inherits="src=image"/> - <xul:image class="downloadTypeIcon blockedIcon"/> - <xul:vbox pack="center" flex="1"> - <xul:description class="downloadDisplayName" - crop="end" - xbl:inherits="value=extendedDisplayName,tooltiptext=extendedDisplayNameTip"/> - <xul:progressmeter anonid="progressmeter" - class="downloadProgress" - min="0" - max="100" - xbl:inherits="mode=progressmode,value=progress"/> - <xul:description class="downloadDetails" - style="width: &downloadDetails.width;" - crop="end" - xbl:inherits="value=status,tooltiptext=statusTip"/> - </xul:vbox> - - <xul:button class="downloadButton downloadCancel" - tooltiptext="&cmd.cancel.label;" - oncommand="goDoCommand('downloadsCmd_cancel')"/> - <xul:button class="downloadButton downloadRetry" - tooltiptext="&cmd.retry.label;" - oncommand="goDoCommand('downloadsCmd_retry')"/> - <xul:button class="downloadButton downloadShow" - tooltiptext="&cmd.show.label;" - oncommand="goDoCommand('downloadsCmd_show')"/> - - </content> - </binding> -</bindings> diff --git a/components/downloads/content/downloads.css b/components/downloads/content/downloads.css deleted file mode 100644 index 825db68..0000000 --- a/components/downloads/content/downloads.css +++ /dev/null @@ -1,132 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/*** Download items ***/ - -richlistitem[type="download"] { - -moz-binding: url('chrome://browser/content/downloads/download.xml#download'); -} - -richlistitem[type="download"]:-moz-any([state="-1"],/* Starting (initial) */ - [state="0"], /* Downloading */ - [state="4"], /* Paused */ - [state="5"], /* Starting (queued) */ - [state="7"]) /* Scanning */ -{ - -moz-binding: url('chrome://browser/content/downloads/download.xml#download-in-progress'); -} - -richlistitem[type="download"]:not([selected]) button { - /* Only focus buttons in the selected item. */ - -moz-user-focus: none; -} - -/*** Visibility of controls inside download items ***/ - -.download-state:-moz-any( [state="6"], /* Blocked (parental) */ - [state="8"], /* Blocked (dirty) */ - [state="9"]) /* Blocked (policy) */ - .downloadTypeIcon:not(.blockedIcon), - -.download-state:not(:-moz-any([state="6"], /* Blocked (parental) */ - [state="8"], /* Blocked (dirty) */ - [state="9"]) /* Blocked (policy) */) - .downloadTypeIcon.blockedIcon, - -.download-state:not(:-moz-any([state="-1"],/* Starting (initial) */ - [state="0"], /* Downloading */ - [state="4"], /* Paused */ - [state="5"], /* Starting (queued) */ - [state="7"]) /* Scanning */) - .downloadProgress, - -.download-state:not( [state="0"] /* Downloading */) - .downloadPauseMenuItem, - -.download-state:not( [state="4"] /* Paused */) - .downloadResumeMenuItem, - -.download-state:not(:-moz-any([state="2"], /* Failed */ - [state="4"]) /* Paused */) - .downloadCancelMenuItem, - -.download-state:not(:-moz-any([state="1"], /* Finished */ - [state="2"], /* Failed */ - [state="3"], /* Canceled */ - [state="6"], /* Blocked (parental) */ - [state="8"], /* Blocked (dirty) */ - [state="9"]) /* Blocked (policy) */) - .downloadRemoveFromHistoryMenuItem, - -.download-state:not(:-moz-any([state="-1"],/* Starting (initial) */ - [state="0"], /* Downloading */ - [state="1"], /* Finished */ - [state="4"], /* Paused */ - [state="5"]) /* Starting (queued) */) - .downloadShowMenuItem, - -.download-state[state="7"] /* Scanning */ .downloadCommandsSeparator - -{ - display: none; -} - -/*** Visibility of download buttons and indicator controls. ***/ - -.download-state:not(:-moz-any([state="-1"],/* Starting (initial) */ - [state="0"], /* Downloading */ - [state="4"], /* Paused */ - [state="5"]) /* Starting (queued) */) - .downloadCancel, - -.download-state:not(:-moz-any([state="2"], /* Failed */ - [state="3"]) /* Canceled */) - .downloadRetry, - -.download-state:not( [state="1"] /* Finished */) - .downloadShow, - -#downloads-indicator:-moz-any([progress], - [counter], - [paused]) #downloads-indicator-icon, - -#downloads-indicator:not(:-moz-any([progress], - [counter], - [paused])) - #downloads-indicator-progress-area - -{ - visibility: hidden; -} - -.download-state[state="1"]:not([exists]) .downloadShow -{ - display: none; -} - -#downloadsSummary:not([inprogress]) > vbox > #downloadsSummaryProgress, -#downloadsSummary:not([inprogress]) > vbox > #downloadsSummaryDetails, -#downloadsFooter[showingsummary] > #downloadsHistory, -#downloadsFooter:not([showingsummary]) > #downloadsSummary -{ - display: none; -} - -/* Hacks for toolbar full and text modes, until bug 573329 removes them */ - -toolbar[mode="text"] > #downloads-indicator { - display: -moz-box; - -moz-box-orient: vertical; - -moz-box-pack: center; -} - -toolbar[mode="text"] > #downloads-indicator > .toolbarbutton-text { - -moz-box-ordinal-group: 1; -} - -toolbar[mode="text"] > #downloads-indicator > .toolbarbutton-icon { - display: -moz-box; - -moz-box-ordinal-group: 2; - visibility: collapse; -} diff --git a/components/downloads/content/downloads.js b/components/downloads/content/downloads.js deleted file mode 100644 index ee1c690..0000000 --- a/components/downloads/content/downloads.js +++ /dev/null @@ -1,1614 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon", - "resource:///modules/DownloadsCommon.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "DownloadsViewUI", - "resource:///modules/DownloadsViewUI.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", - "resource://gre/modules/FileUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", - "resource://gre/modules/NetUtil.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", - "resource://gre/modules/PlacesUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Services", - "resource://gre/modules/Services.jsm"); - -/** - * Handles the Downloads panel user interface for each browser window. - * - * This file includes the following constructors and global objects: - * - * DownloadsPanel - * Main entry point for the downloads panel interface. - * - * DownloadsOverlayLoader - * Allows loading the downloads panel and the status indicator interfaces on - * demand, to improve startup performance. - * - * DownloadsView - * Builds and updates the downloads list widget, responding to changes in the - * download state and real-time data. In addition, handles part of the user - * interaction events raised by the downloads list widget. - * - * DownloadsViewItem - * Builds and updates a single item in the downloads list widget, responding to - * changes in the download state and real-time data. - * - * DownloadsViewController - * Handles part of the user interaction events raised by the downloads list - * widget, in particular the "commands" that apply to multiple items, and - * dispatches the commands that apply to individual items. - * - * DownloadsViewItemController - * Handles all the user interaction events, in particular the "commands", - * related to a single item in the downloads list widgets. - */ - -/** - * A few words on focus and focusrings - * - * We do quite a few hacks in the Downloads Panel for focusrings. In fact, we - * basically suppress most if not all XUL-level focusrings, and style/draw - * them ourselves (using :focus instead of -moz-focusring). There are a few - * reasons for this: - * - * 1) Richlists on OSX don't have focusrings; instead, they are shown as - * selected. This makes for some ambiguity when we have a focused/selected - * item in the list, and the mouse is hovering a completed download (which - * highlights). - * 2) Windows doesn't show focusrings until after the first time that tab is - * pressed (and by then you're focusing the second item in the panel). - * 3) Richlistbox sets -moz-focusring even when we select it with a mouse. - * - * In general, the desired behaviour is to focus the first item after pressing - * tab/down, and show that focus with a ring. Then, if the mouse moves over - * the panel, to hide that focus ring; essentially resetting us to the state - * before pressing the key. - * - * We end up capturing the tab/down key events, and preventing their default - * behaviour. We then set a "keyfocus" attribute on the panel, which allows - * us to draw a ring around the currently focused element. If the panel is - * closed or the mouse moves over the panel, we remove the attribute. - */ - -"use strict"; - -//////////////////////////////////////////////////////////////////////////////// -//// DownloadsPanel - -/** - * Main entry point for the downloads panel interface. - */ -const DownloadsPanel = { - ////////////////////////////////////////////////////////////////////////////// - //// Initialization and termination - - /** - * Internal state of the downloads panel, based on one of the kState - * constants. This is not the same state as the XUL panel element. - */ - _state: 0, - - /** The panel is not linked to downloads data yet. */ - get kStateUninitialized() 0, - /** This object is linked to data, but the panel is invisible. */ - get kStateHidden() 1, - /** The panel will be shown as soon as possible. */ - get kStateWaitingData() 2, - /** The panel is almost shown - we're just waiting to get a handle on the - anchor. */ - get kStateWaitingAnchor() 3, - /** The panel is open. */ - get kStateShown() 4, - - /** - * Location of the panel overlay. - */ - get kDownloadsOverlay() - "chrome://browser/content/downloads/downloadsOverlay.xul", - - /** - * Starts loading the download data in background, without opening the panel. - * Use showPanel instead to load the data and open the panel at the same time. - * - * @param aCallback - * Called when initialization is complete. - */ - initialize: function DP_initialize(aCallback) - { - DownloadsCommon.log("Attempting to initialize DownloadsPanel for a window."); - if (this._state != this.kStateUninitialized) { - DownloadsCommon.log("DownloadsPanel is already initialized."); - DownloadsOverlayLoader.ensureOverlayLoaded(this.kDownloadsOverlay, - aCallback); - return; - } - this._state = this.kStateHidden; - - window.addEventListener("unload", this.onWindowUnload, false); - - // Ensure that the Download Manager service is running. This resumes - // active downloads if required. If there are downloads to be shown in the - // panel, starting the service will make us load their data asynchronously. - DownloadsCommon.initializeAllDataLinks(); - - - // Now that data loading has eventually started, load the required XUL - // elements and initialize our views. - DownloadsCommon.log("Ensuring DownloadsPanel overlay loaded."); - DownloadsOverlayLoader.ensureOverlayLoaded(this.kDownloadsOverlay, - function DP_I_callback() { - DownloadsViewController.initialize(); - DownloadsCommon.log("Attaching DownloadsView..."); - DownloadsCommon.getData(window).addView(DownloadsView); - DownloadsCommon.getSummary(window, DownloadsView.kItemCountLimit) - .addView(DownloadsSummary); - DownloadsCommon.log("DownloadsView attached - the panel for this window", - "should now see download items come in."); - DownloadsPanel._attachEventListeners(); - DownloadsCommon.log("DownloadsPanel initialized."); - aCallback(); - }); - }, - - /** - * Closes the downloads panel and frees the internal resources related to the - * downloads. The downloads panel can be reopened later, even after this - * function has been called. - */ - terminate: function DP_terminate() - { - DownloadsCommon.log("Attempting to terminate DownloadsPanel for a window."); - if (this._state == this.kStateUninitialized) { - DownloadsCommon.log("DownloadsPanel was never initialized. Nothing to do."); - return; - } - - window.removeEventListener("unload", this.onWindowUnload, false); - - // Ensure that the panel is closed before shutting down. - this.hidePanel(); - - DownloadsViewController.terminate(); - DownloadsCommon.getData(window).removeView(DownloadsView); - DownloadsCommon.getSummary(window, DownloadsView.kItemCountLimit) - .removeView(DownloadsSummary); - this._unattachEventListeners(); - - this._state = this.kStateUninitialized; - - DownloadsSummary.active = false; - DownloadsCommon.log("DownloadsPanel terminated."); - }, - - ////////////////////////////////////////////////////////////////////////////// - //// Panel interface - - /** - * Main panel element in the browser window, or null if the panel overlay - * hasn't been loaded yet. - */ - get panel() - { - // If the downloads panel overlay hasn't loaded yet, just return null - // without resetting this.panel. - let downloadsPanel = document.getElementById("downloadsPanel"); - if (!downloadsPanel) - return null; - - delete this.panel; - return this.panel = downloadsPanel; - }, - - /** - * Starts opening the downloads panel interface, anchored to the downloads - * button of the browser window. The list of downloads to display is - * initialized the first time this method is called, and the panel is shown - * only when data is ready. - */ - showPanel: function DP_showPanel() - { - DownloadsCommon.log("Opening the downloads panel."); - - if (this.isPanelShowing) { - DownloadsCommon.log("Panel is already showing - focusing instead."); - this._focusPanel(); - return; - } - - this.initialize(function DP_SP_callback() { - // Delay displaying the panel because this function will sometimes be - // called while another window is closing (like the window for selecting - // whether to save or open the file), and that would cause the panel to - // close immediately. - setTimeout(function () DownloadsPanel._openPopupIfDataReady(), 0); - }.bind(this)); - - DownloadsCommon.log("Waiting for the downloads panel to appear."); - this._state = this.kStateWaitingData; - }, - - /** - * Hides the downloads panel, if visible, but keeps the internal state so that - * the panel can be reopened quickly if required. - */ - hidePanel: function DP_hidePanel() - { - DownloadsCommon.log("Closing the downloads panel."); - - if (!this.isPanelShowing) { - DownloadsCommon.log("Downloads panel is not showing - nothing to do."); - return; - } - - this.panel.hidePopup(); - - // Ensure that we allow the panel to be reopened. Note that, if the popup - // was open, then the onPopupHidden event handler has already updated the - // current state, otherwise we must update the state ourselves. - this._state = this.kStateHidden; - DownloadsCommon.log("Downloads panel is now closed."); - }, - - /** - * Indicates whether the panel is shown or will be shown. - */ - get isPanelShowing() - { - return this._state == this.kStateWaitingData || - this._state == this.kStateWaitingAnchor || - this._state == this.kStateShown; - }, - - /** - * Returns whether the user has started keyboard navigation. - */ - get keyFocusing() - { - return this.panel.hasAttribute("keyfocus"); - }, - - /** - * Set to true if the user has started keyboard navigation, and we should be - * showing focusrings in the panel. Also adds a mousemove event handler to - * the panel which disables keyFocusing. - */ - set keyFocusing(aValue) - { - if (aValue) { - this.panel.setAttribute("keyfocus", "true"); - this.panel.addEventListener("mousemove", this); - } else { - this.panel.removeAttribute("keyfocus"); - this.panel.removeEventListener("mousemove", this); - } - return aValue; - }, - - /** - * Handles the mousemove event for the panel, which disables focusring - * visualization. - */ - handleEvent: function DP_handleEvent(aEvent) - { - if (aEvent.type == "mousemove") { - this.keyFocusing = false; - } - }, - - ////////////////////////////////////////////////////////////////////////////// - //// Callback functions from DownloadsView - - /** - * Called after data loading finished. - */ - onViewLoadCompleted: function DP_onViewLoadCompleted() - { - this._openPopupIfDataReady(); - }, - - ////////////////////////////////////////////////////////////////////////////// - //// User interface event functions - - onWindowUnload: function DP_onWindowUnload() - { - // This function is registered as an event listener, we can't use "this". - DownloadsPanel.terminate(); - }, - - onPopupShown: function DP_onPopupShown(aEvent) - { - // Ignore events raised by nested popups. - if (aEvent.target != aEvent.currentTarget) { - return; - } - - DownloadsCommon.log("Downloads panel has shown."); - this._state = this.kStateShown; - - // Since at most one popup is open at any given time, we can set globally. - DownloadsCommon.getIndicatorData(window).attentionSuppressed = true; - - // Ensure that the first item is selected when the panel is focused. - if (DownloadsView.richListBox.itemCount > 0 && - DownloadsView.richListBox.selectedIndex == -1) { - DownloadsView.richListBox.selectedIndex = 0; - } - - this._focusPanel(); - }, - - onPopupHidden: function DP_onPopupHidden(aEvent) - { - // Ignore events raised by nested popups. - if (aEvent.target != aEvent.currentTarget) { - return; - } - - DownloadsCommon.log("Downloads panel has hidden."); - - // Removes the keyfocus attribute so that we stop handling keyboard - // navigation. - this.keyFocusing = false; - - // Since at most one popup is open at any given time, we can set globally. - DownloadsCommon.getIndicatorData(window).attentionSuppressed = false; - - // Allow the anchor to be hidden. - DownloadsButton.releaseAnchor(); - - // Allow the panel to be reopened. - this._state = this.kStateHidden; - }, - - ////////////////////////////////////////////////////////////////////////////// - //// Related operations - - /** - * Shows or focuses the user interface dedicated to downloads history. - */ - showDownloadsHistory: function DP_showDownloadsHistory() - { - DownloadsCommon.log("Showing download history."); - // Hide the panel before showing another window, otherwise focus will return - // to the browser window when the panel closes automatically. - this.hidePanel(); - - BrowserDownloadsUI(); - }, - - ////////////////////////////////////////////////////////////////////////////// - //// Internal functions - - /** - * Attach event listeners to a panel element. These listeners should be - * removed in _unattachEventListeners. This is called automatically after the - * panel has successfully loaded. - */ - _attachEventListeners: function DP__attachEventListeners() - { - // Handle keydown to support accel-V. - this.panel.addEventListener("keydown", this._onKeyDown.bind(this), false); - // Handle keypress to be able to preventDefault() events before they reach - // the richlistbox, for keyboard navigation. - this.panel.addEventListener("keypress", this._onKeyPress.bind(this), false); - }, - - /** - * Unattach event listeners that were added in _attachEventListeners. This - * is called automatically on panel termination. - */ - _unattachEventListeners: function DP__unattachEventListeners() - { - this.panel.removeEventListener("keydown", this._onKeyDown.bind(this), - false); - this.panel.removeEventListener("keypress", this._onKeyPress.bind(this), - false); - }, - - _onKeyPress: function DP__onKeyPress(aEvent) - { - // Handle unmodified keys only. - if (aEvent.altKey || aEvent.ctrlKey || aEvent.shiftKey || aEvent.metaKey) { - return; - } - - let richListBox = DownloadsView.richListBox; - - // If the user has pressed the tab, up, or down cursor key, start keyboard - // navigation, thus enabling focusrings in the panel. Keyboard navigation - // is automatically disabled if the user moves the mouse on the panel, or - // if the panel is closed. - if ((aEvent.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_TAB || - aEvent.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_UP || - aEvent.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_DOWN) && - !this.keyFocusing) { - this.keyFocusing = true; - // Ensure there's a selection, we will show the focus ring around it and - // prevent the richlistbox from changing the selection. - if (DownloadsView.richListBox.selectedIndex == -1) - DownloadsView.richListBox.selectedIndex = 0; - aEvent.preventDefault(); - return; - } - - if (aEvent.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_DOWN) { - // If the last element in the list is selected, or the footer is already - // focused, focus the footer. - if (richListBox.selectedItem === richListBox.lastChild || - document.activeElement.parentNode.id === "downloadsFooter") { - DownloadsFooter.focus(); - aEvent.preventDefault(); - return; - } - } - - // Pass keypress events to the richlistbox view when it's focused. - if (document.activeElement === richListBox) { - DownloadsView.onDownloadKeyPress(aEvent); - } - }, - - /** - * Keydown listener that listens for the keys to start key focusing, as well - * as the the accel-V "paste" event, which initiates a file download if the - * pasted item can be resolved to a URI. - */ - _onKeyDown: function DP__onKeyDown(aEvent) - { - // If the footer is focused and the downloads list has at least 1 element - // in it, focus the last element in the list when going up. - if (aEvent.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_UP && - document.activeElement.parentNode.id === "downloadsFooter" && - DownloadsView.richListBox.firstChild) { - DownloadsView.richListBox.focus(); - DownloadsView.richListBox.selectedItem = DownloadsView.richListBox.lastChild; - aEvent.preventDefault(); - return; - } - - let pasting = aEvent.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_V && -#ifdef XP_MACOSX - aEvent.metaKey; -#else - aEvent.ctrlKey; -#endif - - if (!pasting) { - return; - } - - DownloadsCommon.log("Received a paste event."); - - let trans = Cc["@mozilla.org/widget/transferable;1"] - .createInstance(Ci.nsITransferable); - trans.init(null); - let flavors = ["text/x-moz-url", "text/unicode"]; - flavors.forEach(trans.addDataFlavor); - Services.clipboard.getData(trans, Services.clipboard.kGlobalClipboard); - // Getting the data or creating the nsIURI might fail - try { - let data = {}; - trans.getAnyTransferData({}, data, {}); - let [url, name] = data.value - .QueryInterface(Ci.nsISupportsString) - .data - .split("\n"); - if (!url) { - return; - } - - let uri = NetUtil.newURI(url); - DownloadsCommon.log("Pasted URL seems valid. Starting download."); - DownloadURL(uri.spec, name, document); - } catch (ex) {} - }, - - /** - * Move focus to the main element in the downloads panel, unless another - * element in the panel is already focused. - */ - _focusPanel: function DP_focusPanel() - { - // We may be invoked while the panel is still waiting to be shown. - if (this._state != this.kStateShown) { - return; - } - - let element = document.commandDispatcher.focusedElement; - while (element && element != this.panel) { - element = element.parentNode; - } - if (!element) { - if (DownloadsView.richListBox.itemCount > 0) { - DownloadsView.richListBox.focus(); - } else { - DownloadsFooter.focus(); - } - } - }, - - /** - * Opens the downloads panel when data is ready to be displayed. - */ - _openPopupIfDataReady: function DP_openPopupIfDataReady() - { - // We don't want to open the popup if we already displayed it, or if we are - // still loading data. - if (this._state != this.kStateWaitingData || DownloadsView.loading) { - return; - } - - this._state = this.kStateWaitingAnchor; - - // Ensure the anchor is visible. If that is not possible, show the panel - // anchored to the top area of the window, near the default anchor position. - DownloadsButton.getAnchor(function DP_OPIDR_callback(aAnchor) { - // If somehow we've switched states already (by getting a panel hiding - // event before an overlay is loaded, for example), bail out. - if (this._state != this.kStateWaitingAnchor) - return; - - // At this point, if the window is minimized, opening the panel could fail - // without any notification, and there would be no way to either open or - // close the panel any more. To prevent this, check if the window is - // minimized and in that case force the panel to the closed state. - if (window.windowState == Ci.nsIDOMChromeWindow.STATE_MINIMIZED) { - DownloadsButton.releaseAnchor(); - this._state = this.kStateHidden; - return; - } - - // When the panel is opened, we check if the target files of visible items - // still exist, and update the allowed items interactions accordingly. We - // do these checks on a background thread, and don't prevent the panel to - // be displayed while these checks are being performed. - for (let viewItem of DownloadsView._visibleViewItems.values()) { - viewItem.download.refresh().catch(Cu.reportError); - } - - if (aAnchor) { - DownloadsCommon.log("Opening downloads panel popup."); - this.panel.openPopup(aAnchor, "bottomcenter topright", 0, 0, false, - null); - } else { - DownloadsCommon.error("We can't find the anchor! Failure case - opening", - "downloads panel on TabsToolbar. We should never", - "get here!"); - Components.utils.reportError( - "Downloads button cannot be found"); - } - }.bind(this)); - } -}; - -XPCOMUtils.defineConstant(this, "DownloadsPanel", DownloadsPanel); - -//////////////////////////////////////////////////////////////////////////////// -//// DownloadsOverlayLoader - -/** - * Allows loading the downloads panel and the status indicator interfaces on - * demand, to improve startup performance. - */ -const DownloadsOverlayLoader = { - /** - * We cannot load two overlays at the same time, thus we use a queue of - * pending load requests. - */ - _loadRequests: [], - - /** - * True while we are waiting for an overlay to be loaded. - */ - _overlayLoading: false, - - /** - * This object has a key for each overlay URI that is already loaded. - */ - _loadedOverlays: {}, - - /** - * Loads the specified overlay and invokes the given callback when finished. - * - * @param aOverlay - * String containing the URI of the overlay to load in the current - * window. If this overlay has already been loaded using this - * function, then the overlay is not loaded again. - * @param aCallback - * Invoked when loading is completed. If the overlay is already - * loaded, the function is called immediately. - */ - ensureOverlayLoaded: function DOL_ensureOverlayLoaded(aOverlay, aCallback) - { - // The overlay is already loaded, invoke the callback immediately. - if (aOverlay in this._loadedOverlays) { - aCallback(); - return; - } - - // The callback will be invoked when loading is finished. - this._loadRequests.push({ overlay: aOverlay, callback: aCallback }); - if (this._overlayLoading) { - return; - } - - function DOL_EOL_loadCallback() { - this._overlayLoading = false; - this._loadedOverlays[aOverlay] = true; - - // Loading the overlay causes all the persisted XUL attributes to be - // reapplied, including "iconsize" on the toolbars. Until bug 640158 is - // fixed, we must recalculate the correct "iconsize" attributes manually. - retrieveToolbarIconsizesFromTheme(); - - this.processPendingRequests(); - } - - this._overlayLoading = true; - DownloadsCommon.log("Loading overlay ", aOverlay); - document.loadOverlay(aOverlay, DOL_EOL_loadCallback.bind(this)); - }, - - /** - * Re-processes all the currently pending requests, invoking the callbacks - * and/or loading more overlays as needed. In most cases, there will be a - * single request for one overlay, that will be processed immediately. - */ - processPendingRequests: function DOL_processPendingRequests() - { - // Re-process all the currently pending requests, yet allow more requests - // to be appended at the end of the array if we're not ready for them. - let currentLength = this._loadRequests.length; - for (let i = 0; i < currentLength; i++) { - let request = this._loadRequests.shift(); - - // We must call ensureOverlayLoaded again for each request, to check if - // the associated callback can be invoked now, or if we must still wait - // for the associated overlay to load. - this.ensureOverlayLoaded(request.overlay, request.callback); - } - } -}; - -XPCOMUtils.defineConstant(this, "DownloadsOverlayLoader", DownloadsOverlayLoader); - -//////////////////////////////////////////////////////////////////////////////// -//// DownloadsView - -/** - * Builds and updates the downloads list widget, responding to changes in the - * download state and real-time data. In addition, handles part of the user - * interaction events raised by the downloads list widget. - */ -const DownloadsView = { - ////////////////////////////////////////////////////////////////////////////// - //// Functions handling download items in the list - - /** - * Maximum number of items shown by the list at any given time. - */ - kItemCountLimit: 3, - - /** - * Indicates whether we are still loading downloads data asynchronously. - */ - loading: false, - - /** - * Ordered array of all Download objects. We need to keep this array because - * only a limited number of items are shown at once, and if an item that is - * currently visible is removed from the list, we might need to take another - * item from the array and make it appear at the bottom. - */ - _downloads: [], - - /** - * Associates the visible Download objects with their corresponding - * DownloadsViewItem object. There is a limited number of view items in the - * panel at any given time. - */ - _visibleViewItems: new Map(), - - /** - * Called when the number of items in the list changes. - */ - _itemCountChanged: function DV_itemCountChanged() - { - DownloadsCommon.log("The downloads item count has changed - we are tracking", - this._downloads.length, "downloads in total."); - let count = this._downloads.length; - let hiddenCount = count - this.kItemCountLimit; - - if (count > 0) { - DownloadsCommon.log("Setting the panel's hasdownloads attribute to true."); - DownloadsPanel.panel.setAttribute("hasdownloads", "true"); - } else { - DownloadsCommon.log("Removing the panel's hasdownloads attribute."); - DownloadsPanel.panel.removeAttribute("hasdownloads"); - } - - // If we've got some hidden downloads, we should activate the - // DownloadsSummary. The DownloadsSummary will determine whether or not - // it's appropriate to actually display the summary. - DownloadsSummary.active = hiddenCount > 0; - }, - - /** - * Element corresponding to the list of downloads. - */ - get richListBox() - { - delete this.richListBox; - return this.richListBox = document.getElementById("downloadsListBox"); - }, - - /** - * Element corresponding to the button for showing more downloads. - */ - get downloadsHistory() - { - delete this.downloadsHistory; - return this.downloadsHistory = document.getElementById("downloadsHistory"); - }, - - ////////////////////////////////////////////////////////////////////////////// - //// Callback functions from DownloadsData - - /** - * Called before multiple downloads are about to be loaded. - */ - onDataLoadStarting: function DV_onDataLoadStarting() - { - DownloadsCommon.log("onDataLoadStarting called for DownloadsView."); - this.loading = true; - }, - - /** - * Called after data loading finished. - */ - onDataLoadCompleted: function DV_onDataLoadCompleted() - { - DownloadsCommon.log("onDataLoadCompleted called for DownloadsView."); - - this.loading = false; - - // We suppressed item count change notifications during the batch load, at - // this point we should just call the function once. - this._itemCountChanged(); - - // Notify the panel that all the initially available downloads have been - // loaded. This ensures that the interface is visible, if still required. - DownloadsPanel.onViewLoadCompleted(); - }, - - /** - * Called when the downloads database becomes unavailable (for example, - * entering Private Browsing Mode). References to existing data should be - * discarded. - */ - onDataInvalidated: function DV_onDataInvalidated() - { - DownloadsCommon.log("Downloads data has been invalidated. Cleaning up", - "DownloadsView."); - - DownloadsPanel.terminate(); - - // Clear the list by replacing with a shallow copy. - let emptyView = this.richListBox.cloneNode(false); - this.richListBox.parentNode.replaceChild(emptyView, this.richListBox); - this.richListBox = emptyView; - this._viewItems = {}; - this._dataItems = []; - }, - - /** - * Called when a new download data item is available, either during the - * asynchronous data load or when a new download is started. - * - * @param aDownload - * Download object that was just added. - * @param aNewest - * When true, indicates that this item is the most recent and should be - * added in the topmost position. This happens when a new download is - * started. When false, indicates that the item is the least recent - * and should be appended. The latter generally happens during the - * asynchronous data load. - */ - onDownloadAdded(download, aNewest) { - DownloadsCommon.log("A new download data item was added - aNewest =", - aNewest); - - if (aNewest) { - this._downloads.unshift(download); - } else { - this._downloads.push(download); - } - - let itemsNowOverflow = this._downloads.length > this.kItemCountLimit; - if (aNewest || !itemsNowOverflow) { - // The newly added item is visible in the panel and we must add the - // corresponding element. This is either because it is the first item, or - // because it was added at the bottom but the list still doesn't overflow. - this._addViewItem(download, aNewest); - } - if (aNewest && itemsNowOverflow) { - // If the list overflows, remove the last item from the panel to make room - // for the new one that we just added at the top. - this._removeViewItem(this._downloads[this.kItemCountLimit]); - } - - // For better performance during batch loads, don't update the count for - // every item, because the interface won't be visible until load finishes. - if (!this.loading) { - this._itemCountChanged(); - } - }, - - onDownloadStateChanged(download) { - let viewItem = this._visibleViewItems.get(download); - if (viewItem) { - viewItem.onStateChanged(); - } - }, - - onDownloadChanged(download) { - let viewItem = this._visibleViewItems.get(download); - if (viewItem) { - viewItem.onChanged(); - } - }, - - /** - * Called when a data item is removed. Ensures that the widget associated - * with the view item is removed from the user interface. - * - * @param download - * Download object that is being removed. - */ - onDownloadRemoved(download) { - DownloadsCommon.log("A download data item was removed."); - - let itemIndex = this._downloads.indexOf(download); - this._downloads.splice(itemIndex, 1); - - if (itemIndex < this.kItemCountLimit) { - // The item to remove is visible in the panel. - this._removeViewItem(download); - if (this._downloads.length >= this.kItemCountLimit) { - // Reinsert the next item into the panel. - this._addViewItem(this._downloads[this.kItemCountLimit - 1], false); - } - } - - this._itemCountChanged(); - }, - - /** - * Associates each richlistitem for a download with its corresponding - * DownloadsViewItemController object. - */ - _controllersForElements: new Map(), - - controllerForElement(element) { - return this._controllersForElements.get(element); - }, - - /** - * Creates a new view item associated with the specified data item, and adds - * it to the top or the bottom of the list. - */ - _addViewItem(download, aNewest) - { - DownloadsCommon.log("Adding a new DownloadsViewItem to the downloads list.", - "aNewest =", aNewest); - - let element = document.createElement("richlistitem"); - let viewItem = new DownloadsViewItem(download, element); - this._visibleViewItems.set(download, viewItem); - let viewItemController = new DownloadsViewItemController(download); - this._controllersForElements.set(element, viewItemController); - if (aNewest) { - this.richListBox.insertBefore(element, this.richListBox.firstChild); - } else { - this.richListBox.appendChild(element); - } - }, - - /** - * Removes the view item associated with the specified data item. - */ - _removeViewItem(download) { - DownloadsCommon.log("Removing a DownloadsViewItem from the downloads list."); - let element = this._visibleViewItems.get(download).element; - let previousSelectedIndex = this.richListBox.selectedIndex; - this.richListBox.removeChild(element); - if (previousSelectedIndex != -1) { - this.richListBox.selectedIndex = Math.min(previousSelectedIndex, - this.richListBox.itemCount - 1); - } - this._visibleViewItems.delete(download); - this._controllersForElements.delete(element); - }, - - ////////////////////////////////////////////////////////////////////////////// - //// User interface event functions - - /** - * Helper function to do commands on a specific download item. - * - * @param aEvent - * Event object for the event being handled. If the event target is - * not a richlistitem that represents a download, this function will - * walk up the parent nodes until it finds a DOM node that is. - * @param aCommand - * The command to be performed. - */ - onDownloadCommand: function DV_onDownloadCommand(aEvent, aCommand) - { - let target = aEvent.target; - while (target.nodeName != "richlistitem") { - target = target.parentNode; - } - DownloadsView.controllerForElement(target).doCommand(aCommand); - }, - - onDownloadClick: function DV_onDownloadClick(aEvent) - { - // Handle primary clicks only, and exclude the action button. - if (aEvent.button == 0 && - !aEvent.originalTarget.hasAttribute("oncommand")) { - goDoCommand("downloadsCmd_open"); - } - }, - - /** - * Handles keypress events on a download item. - */ - onDownloadKeyPress: function DV_onDownloadKeyPress(aEvent) - { - // Pressing the key on buttons should not invoke the action because the - // event has already been handled by the button itself. - if (aEvent.originalTarget.hasAttribute("command") || - aEvent.originalTarget.hasAttribute("oncommand")) { - return; - } - - if (aEvent.charCode == " ".charCodeAt(0)) { - goDoCommand("downloadsCmd_pauseResume"); - return; - } - - if (aEvent.keyCode == KeyEvent.DOM_VK_ENTER || - aEvent.keyCode == KeyEvent.DOM_VK_RETURN) { - goDoCommand("downloadsCmd_doDefault"); - } - }, - - - /** - * Mouse listeners to handle selection on hover. - */ - onDownloadMouseOver: function DV_onDownloadMouseOver(aEvent) - { - if (aEvent.originalTarget.parentNode == this.richListBox) - this.richListBox.selectedItem = aEvent.originalTarget; - }, - onDownloadMouseOut: function DV_onDownloadMouseOut(aEvent) - { - if (aEvent.originalTarget.parentNode == this.richListBox) { - // If the destination element is outside of the richlistitem, clear the - // selection. - let element = aEvent.relatedTarget; - while (element && element != aEvent.originalTarget) { - element = element.parentNode; - } - if (!element) - this.richListBox.selectedIndex = -1; - } - }, - - onDownloadContextMenu: function DV_onDownloadContextMenu(aEvent) - { - let element = this.richListBox.selectedItem; - if (!element) { - return; - } - - DownloadsViewController.updateCommands(); - - // Set the state attribute so that only the appropriate items are displayed. - let contextMenu = document.getElementById("downloadsContextMenu"); - contextMenu.setAttribute("state", element.getAttribute("state")); - }, - - onDownloadDragStart: function DV_onDownloadDragStart(aEvent) - { - let element = this.richListBox.selectedItem; - if (!element) { - return; - } - - // We must check for existence synchronously because this is a DOM event. - let localFile = new FileUtils.File(DownloadsView.controllerForElement(element) - .download.target.path); - if (!localFile.exists()) { - return; - } - - let dataTransfer = aEvent.dataTransfer; - dataTransfer.mozSetDataAt("application/x-moz-file", localFile, 0); - dataTransfer.effectAllowed = "copyMove"; - var url = Services.io.newFileURI(localFile).spec; - dataTransfer.setData("text/uri-list", url); - dataTransfer.setData("text/plain", url); - dataTransfer.addElement(element); - - aEvent.stopPropagation(); - } -} - -XPCOMUtils.defineConstant(this, "DownloadsView", DownloadsView); - -//////////////////////////////////////////////////////////////////////////////// -//// DownloadsViewItem - -/** - * Builds and updates a single item in the downloads list widget, responding to - * changes in the download state and real-time data. - * - * @param download - * Download object to be associated with the view item. - * @param aElement - * XUL element corresponding to the single download item in the view. - */ -function DownloadsViewItem(download, aElement) { - this.download = download; - - this.element = aElement; - this.element._shell = this; - - this.element.setAttribute("type", "download"); - this.element.classList.add("download-state"); - - this._updateState(); -} - -DownloadsViewItem.prototype = { - __proto__: DownloadsViewUI.DownloadElementShell.prototype, - - /** - * The XUL element corresponding to the associated richlistbox item. - */ - _element: null, - - onStateChanged() { - this.element.setAttribute("image", this.image); - this.element.setAttribute("state", - DownloadsCommon.stateOfDownload(this.download)); - }, - - onChanged() { - this._updateProgress(); - }, -}; - -//////////////////////////////////////////////////////////////////////////////// -//// DownloadsViewController - -/** - * Handles part of the user interaction events raised by the downloads list - * widget, in particular the "commands" that apply to multiple items, and - * dispatches the commands that apply to individual items. - */ -const DownloadsViewController = { - ////////////////////////////////////////////////////////////////////////////// - //// Initialization and termination - - initialize: function DVC_initialize() - { - window.controllers.insertControllerAt(0, this); - }, - - terminate: function DVC_terminate() - { - window.controllers.removeController(this); - }, - - ////////////////////////////////////////////////////////////////////////////// - //// nsIController - - supportsCommand: function DVC_supportsCommand(aCommand) - { - // Firstly, determine if this is a command that we can handle. - if (!(aCommand in this.commands) && - !(aCommand in DownloadsViewItemController.prototype.commands)) { - return false; - } - // Secondly, determine if focus is on a control in the downloads list. - let element = document.commandDispatcher.focusedElement; - while (element && element != DownloadsView.richListBox) { - element = element.parentNode; - } - // We should handle the command only if the downloads list is among the - // ancestors of the focused element. - return !!element; - }, - - isCommandEnabled: function DVC_isCommandEnabled(aCommand) - { - // Handle commands that are not selection-specific. - if (aCommand == "downloadsCmd_clearList") { - return DownloadsCommon.getData(window).canRemoveFinished; - } - - // Other commands are selection-specific. - let element = DownloadsView.richListBox.selectedItem; - return element && DownloadsView.controllerForElement(element) - .isCommandEnabled(aCommand); - }, - - doCommand: function DVC_doCommand(aCommand) - { - // If this command is not selection-specific, execute it. - if (aCommand in this.commands) { - this.commands[aCommand].apply(this); - return; - } - - // Other commands are selection-specific. - let element = DownloadsView.richListBox.selectedItem; - if (element) { - // The doCommand function also checks if the command is enabled. - DownloadsView.controllerForElement(element).doCommand(aCommand); - } - }, - - onEvent: function () { }, - - ////////////////////////////////////////////////////////////////////////////// - //// Other functions - - updateCommands: function DVC_updateCommands() - { - Object.keys(this.commands).forEach(goUpdateCommand); - Object.keys(DownloadsViewItemController.prototype.commands) - .forEach(goUpdateCommand); - }, - - ////////////////////////////////////////////////////////////////////////////// - //// Selection-independent commands - - /** - * This object contains one key for each command that operates regardless of - * the currently selected item in the list. - */ - commands: { - downloadsCmd_clearList: function DVC_downloadsCmd_clearList() - { - DownloadsCommon.getData(window).removeFinished(); - } - } -}; - -XPCOMUtils.defineConstant(this, "DownloadsViewController", DownloadsViewController); - -//////////////////////////////////////////////////////////////////////////////// -//// DownloadsViewItemController - -/** - * Handles all the user interaction events, in particular the "commands", - * related to a single item in the downloads list widgets. - */ -function DownloadsViewItemController(download) { - this.download = download; -} - -DownloadsViewItemController.prototype = { - isCommandEnabled: function DVIC_isCommandEnabled(aCommand) - { - switch (aCommand) { - case "downloadsCmd_open": { - if (!this.download.succeeded) { - return false; - } - - let file = new FileUtils.File(this.download.target.path); - return file.exists(); - } - case "downloadsCmd_show": { - let file = new FileUtils.File(this.download.target.path); - if (file.exists()) { - return true; - } - - if (!this.download.target.partFilePath) { - return false; - } - - let partFile = new FileUtils.File(this.download.target.partFilePath); - return partFile.exists(); - } - case "downloadsCmd_pauseResume": - return this.download.hasPartialData && !this.download.error; - case "downloadsCmd_retry": - return this.download.canceled || this.download.error; - case "downloadsCmd_openReferrer": - return !!this.download.source.referrer; - case "cmd_delete": - case "downloadsCmd_cancel": - case "downloadsCmd_copyLocation": - case "downloadsCmd_doDefault": - return true; - } - return false; - }, - - doCommand: function DVIC_doCommand(aCommand) - { - if (this.isCommandEnabled(aCommand)) { - this.commands[aCommand].apply(this); - } - }, - - ////////////////////////////////////////////////////////////////////////////// - //// Item commands - - /** - * This object contains one key for each command that operates on this item. - * - * In commands, the "this" identifier points to the controller item. - */ - commands: { - cmd_delete: function DVIC_cmd_delete() - { - DownloadsCommon.removeAndFinalizeDownload(this.download); - PlacesUtils.bhistory.removePage( - NetUtil.newURI(this.download.source.url)); - }, - - downloadsCmd_cancel: function DVIC_downloadsCmd_cancel() - { - this.download.cancel().catch(() => {}); - this.download.removePartialData().catch(Cu.reportError); - }, - - downloadsCmd_open: function DVIC_downloadsCmd_open() - { - this.download.launch().catch(Cu.reportError); - - // We explicitly close the panel here to give the user the feedback that - // their click has been received, and we're handling the action. - // Otherwise, we'd have to wait for the file-type handler to execute - // before the panel would close. This also helps to prevent the user from - // accidentally opening a file several times. - DownloadsPanel.hidePanel(); - }, - - downloadsCmd_show: function DVIC_downloadsCmd_show() - { - let file = new FileUtils.File(this.download.target.path); - DownloadsCommon.showDownloadedFile(file); - - // We explicitly close the panel here to give the user the feedback that - // their click has been received, and we're handling the action. - // Otherwise, we'd have to wait for the operating system file manager - // window to open before the panel closed. This also helps to prevent the - // user from opening the containing folder several times. - DownloadsPanel.hidePanel(); - }, - - downloadsCmd_pauseResume: function DVIC_downloadsCmd_pauseResume() - { - if (this.download.stopped) { - this.download.start(); - } else { - this.download.cancel(); - } - }, - - downloadsCmd_retry: function DVIC_downloadsCmd_retry() - { - this.download.start().catch(() => {}); - }, - - downloadsCmd_openReferrer: function DVIC_downloadsCmd_openReferrer() - { - openURL(this.download.source.referrer); - }, - - downloadsCmd_copyLocation: function DVIC_downloadsCmd_copyLocation() - { - let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"] - .getService(Ci.nsIClipboardHelper); - clipboard.copyString(this.download.source.url, document); - }, - - downloadsCmd_doDefault: function DVIC_downloadsCmd_doDefault() - { - const nsIDM = Ci.nsIDownloadManager; - - // Determine the default command for the current item. - let defaultCommand = function () { - switch (DownloadsCommon.stateOfDownload(this.download)) { - case nsIDM.DOWNLOAD_NOTSTARTED: return "downloadsCmd_cancel"; - case nsIDM.DOWNLOAD_FINISHED: return "downloadsCmd_open"; - case nsIDM.DOWNLOAD_FAILED: return "downloadsCmd_retry"; - case nsIDM.DOWNLOAD_CANCELED: return "downloadsCmd_retry"; - case nsIDM.DOWNLOAD_PAUSED: return "downloadsCmd_pauseResume"; - case nsIDM.DOWNLOAD_QUEUED: return "downloadsCmd_cancel"; - case nsIDM.DOWNLOAD_BLOCKED_PARENTAL: return "downloadsCmd_openReferrer"; - case nsIDM.DOWNLOAD_SCANNING: return "downloadsCmd_show"; - case nsIDM.DOWNLOAD_DIRTY: return "downloadsCmd_openReferrer"; - case nsIDM.DOWNLOAD_BLOCKED_POLICY: return "downloadsCmd_openReferrer"; - } - return ""; - }.apply(this); - if (defaultCommand && this.isCommandEnabled(defaultCommand)) - this.doCommand(defaultCommand); - } - } -}; - - -//////////////////////////////////////////////////////////////////////////////// -//// DownloadsSummary - -/** - * Manages the summary at the bottom of the downloads panel list if the number - * of items in the list exceeds the panels limit. - */ -const DownloadsSummary = { - - /** - * Sets the active state of the summary. When active, the summary subscribes - * to the DownloadsCommon DownloadsSummaryData singleton. - * - * @param aActive - * Set to true to activate the summary. - */ - set active(aActive) - { - if (aActive == this._active || !this._summaryNode) { - return this._active; - } - if (aActive) { - DownloadsCommon.getSummary(window, DownloadsView.kItemCountLimit) - .refreshView(this); - } else { - DownloadsFooter.showingSummary = false; - } - - return this._active = aActive; - }, - - /** - * Returns the active state of the downloads summary. - */ - get active() this._active, - - _active: false, - - /** - * Sets whether or not we show the progress bar. - * - * @param aShowingProgress - * True if we should show the progress bar. - */ - set showingProgress(aShowingProgress) - { - if (aShowingProgress) { - this._summaryNode.setAttribute("inprogress", "true"); - } else { - this._summaryNode.removeAttribute("inprogress"); - } - // If progress isn't being shown, then we simply do not show the summary. - return DownloadsFooter.showingSummary = aShowingProgress; - }, - - /** - * Sets the amount of progress that is visible in the progress bar. - * - * @param aValue - * A value between 0 and 100 to represent the progress of the - * summarized downloads. - */ - set percentComplete(aValue) - { - if (this._progressNode) { - this._progressNode.setAttribute("value", aValue); - } - return aValue; - }, - - /** - * Sets the description for the download summary. - * - * @param aValue - * A string representing the description of the summarized - * downloads. - */ - set description(aValue) - { - if (this._descriptionNode) { - this._descriptionNode.setAttribute("value", aValue); - this._descriptionNode.setAttribute("tooltiptext", aValue); - } - return aValue; - }, - - /** - * Sets the details for the download summary, such as the time remaining, - * the amount of bytes transferred, etc. - * - * @param aValue - * A string representing the details of the summarized - * downloads. - */ - set details(aValue) - { - if (this._detailsNode) { - this._detailsNode.setAttribute("value", aValue); - this._detailsNode.setAttribute("tooltiptext", aValue); - } - return aValue; - }, - - /** - * Focuses the root element of the summary. - */ - focus: function() - { - if (this._summaryNode) { - this._summaryNode.focus(); - } - }, - - /** - * Respond to keydown events on the Downloads Summary node. - * - * @param aEvent - * The keydown event being handled. - */ - onKeyDown: function DS_onKeyDown(aEvent) - { - if (aEvent.charCode == " ".charCodeAt(0) || - aEvent.keyCode == KeyEvent.DOM_VK_ENTER || - aEvent.keyCode == KeyEvent.DOM_VK_RETURN) { - DownloadsPanel.showDownloadsHistory(); - } - }, - - /** - * Respond to click events on the Downloads Summary node. - * - * @param aEvent - * The click event being handled. - */ - onClick: function DS_onClick(aEvent) - { - DownloadsPanel.showDownloadsHistory(); - }, - - /** - * Element corresponding to the root of the downloads summary. - */ - get _summaryNode() - { - let node = document.getElementById("downloadsSummary"); - if (!node) { - return null; - } - delete this._summaryNode; - return this._summaryNode = node; - }, - - /** - * Element corresponding to the progress bar in the downloads summary. - */ - get _progressNode() - { - let node = document.getElementById("downloadsSummaryProgress"); - if (!node) { - return null; - } - delete this._progressNode; - return this._progressNode = node; - }, - - /** - * Element corresponding to the main description of the downloads - * summary. - */ - get _descriptionNode() - { - let node = document.getElementById("downloadsSummaryDescription"); - if (!node) { - return null; - } - delete this._descriptionNode; - return this._descriptionNode = node; - }, - - /** - * Element corresponding to the secondary description of the downloads - * summary. - */ - get _detailsNode() - { - let node = document.getElementById("downloadsSummaryDetails"); - if (!node) { - return null; - } - delete this._detailsNode; - return this._detailsNode = node; - } -}; - -XPCOMUtils.defineConstant(this, "DownloadsSummary", DownloadsSummary); - -//////////////////////////////////////////////////////////////////////////////// -//// DownloadsFooter - -/** - * Manages events sent to to the footer vbox, which contains both the - * DownloadsSummary as well as the "Show All Downloads" button. - */ -const DownloadsFooter = { - - /** - * Focuses the appropriate element within the footer. If the summary - * is visible, focus it. If not, focus the "Show All Downloads" - * button. - */ - focus: function DF_focus() - { - if (this._showingSummary) { - DownloadsSummary.focus(); - } else { - DownloadsView.downloadsHistory.focus(); - } - }, - - _showingSummary: false, - - /** - * Sets whether or not the Downloads Summary should be displayed in the - * footer. If not, the "Show All Downloads" button is shown instead. - */ - set showingSummary(aValue) - { - if (this._footerNode) { - if (aValue) { - this._footerNode.setAttribute("showingsummary", "true"); - } else { - this._footerNode.removeAttribute("showingsummary"); - } - this._showingSummary = aValue; - } - return aValue; - }, - - /** - * Element corresponding to the footer of the downloads panel. - */ - get _footerNode() - { - let node = document.getElementById("downloadsFooter"); - if (!node) { - return null; - } - delete this._footerNode; - return this._footerNode = node; - } -}; - -XPCOMUtils.defineConstant(this, "DownloadsFooter", DownloadsFooter); diff --git a/components/downloads/content/downloadsOverlay.xul b/components/downloads/content/downloadsOverlay.xul deleted file mode 100644 index ca35ee3..0000000 --- a/components/downloads/content/downloadsOverlay.xul +++ /dev/null @@ -1,142 +0,0 @@ -<?xml version="1.0"?> -# -*- Mode: HTML; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- -# vim: set ts=2 et sw=2 tw=80: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. - -<?xml-stylesheet href="chrome://browser/content/downloads/downloads.css"?> -<?xml-stylesheet href="chrome://browser/skin/downloads/downloads.css"?> - -<!DOCTYPE overlay SYSTEM "chrome://browser/locale/downloads/downloads.dtd"> - -<overlay xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - id="downloadsOverlay"> - - <commandset> - <command id="downloadsCmd_doDefault" - oncommand="goDoCommand('downloadsCmd_doDefault')"/> - <command id="downloadsCmd_pauseResume" - oncommand="goDoCommand('downloadsCmd_pauseResume')"/> - <command id="downloadsCmd_cancel" - oncommand="goDoCommand('downloadsCmd_cancel')"/> - <command id="downloadsCmd_open" - oncommand="goDoCommand('downloadsCmd_open')"/> - <command id="downloadsCmd_show" - oncommand="goDoCommand('downloadsCmd_show')"/> - <command id="downloadsCmd_retry" - oncommand="goDoCommand('downloadsCmd_retry')"/> - <command id="downloadsCmd_openReferrer" - oncommand="goDoCommand('downloadsCmd_openReferrer')"/> - <command id="downloadsCmd_copyLocation" - oncommand="goDoCommand('downloadsCmd_copyLocation')"/> - <command id="downloadsCmd_clearList" - oncommand="goDoCommand('downloadsCmd_clearList')"/> - </commandset> - - <popupset id="mainPopupSet"> - <!-- The panel has level="top" to ensure that it is never hidden by the - taskbar on Windows. See bug 672365. For accessibility to screen - readers, we use a label on the panel instead of the anchor because the - panel can also be displayed without an anchor. --> - <panel id="downloadsPanel" - aria-label="&downloads.title;" - role="group" - type="arrow" - orient="vertical" - level="top" - consumeoutsideclicks="true" - onpopupshown="DownloadsPanel.onPopupShown(event);" - onpopuphidden="DownloadsPanel.onPopupHidden(event);"> - <!-- The following popup menu should be a child of the panel element, - otherwise flickering may occur when the cursor is moved over the area - of a disabled menu item that overlaps the panel. See bug 492960. --> - <menupopup id="downloadsContextMenu" - class="download-state"> - <menuitem command="downloadsCmd_pauseResume" - class="downloadPauseMenuItem" - label="&cmd.pause.label;" - accesskey="&cmd.pause.accesskey;"/> - <menuitem command="downloadsCmd_pauseResume" - class="downloadResumeMenuItem" - label="&cmd.resume.label;" - accesskey="&cmd.resume.accesskey;"/> - <menuitem command="downloadsCmd_cancel" - class="downloadCancelMenuItem" - label="&cmd.cancel.label;" - accesskey="&cmd.cancel.accesskey;"/> - <menuitem command="cmd_delete" - class="downloadRemoveFromHistoryMenuItem" - label="&cmd.removeFromHistory.label;" - accesskey="&cmd.removeFromHistory.accesskey;"/> - <menuitem command="downloadsCmd_show" - class="downloadShowMenuItem" -#ifdef XP_MACOSX - label="&cmd.showMac.label;" - accesskey="&cmd.showMac.accesskey;" -#else - label="&cmd.show.label;" - accesskey="&cmd.show.accesskey;" -#endif - /> - - <menuseparator class="downloadCommandsSeparator"/> - - <menuitem command="downloadsCmd_openReferrer" - label="&cmd.goToDownloadPage.label;" - accesskey="&cmd.goToDownloadPage.accesskey;"/> - <menuitem command="downloadsCmd_copyLocation" - label="&cmd.copyDownloadLink.label;" - accesskey="&cmd.copyDownloadLink.accesskey;"/> - - <menuseparator/> - - <menuitem command="downloadsCmd_clearList" - label="&cmd.clearList.label;" - accesskey="&cmd.clearList.accesskey;"/> - </menupopup> - - <richlistbox id="downloadsListBox" - class="plain" - flex="1" - context="downloadsContextMenu" - onmouseover="DownloadsView.onDownloadMouseOver(event);" - onmouseout="DownloadsView.onDownloadMouseOut(event);" - oncontextmenu="DownloadsView.onDownloadContextMenu(event);" - ondragstart="DownloadsView.onDownloadDragStart(event);"/> - <description id="emptyDownloads" - mousethrough="always"> - &downloadsPanelEmpty.label; - </description> - - <vbox id="downloadsFooter"> - <hbox id="downloadsSummary" - align="center" - orient="horizontal" - onkeydown="DownloadsSummary.onKeyDown(event);" - onclick="DownloadsSummary.onClick(event);"> - <image class="downloadTypeIcon" /> - <vbox> - <description id="downloadsSummaryDescription" - style="min-width: &downloadsSummary.minWidth2;"/> - <progressmeter id="downloadsSummaryProgress" - class="downloadProgress" - min="0" - max="100" - mode="normal" /> - <description id="downloadsSummaryDetails" - style="width: &downloadDetails.width;" - crop="end"/> - </vbox> - </hbox> - - <button id="downloadsHistory" - class="plain" - label="&downloadsHistory.label;" - accesskey="&downloadsHistory.accesskey;" - oncommand="DownloadsPanel.showDownloadsHistory();"/> - </vbox> - </panel> - </popupset> -</overlay> diff --git a/components/downloads/content/indicator.js b/components/downloads/content/indicator.js deleted file mode 100644 index 1a2175a..0000000 --- a/components/downloads/content/indicator.js +++ /dev/null @@ -1,609 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Handles the indicator that displays the progress of ongoing downloads, which - * is also used as the anchor for the downloads panel. - * - * This module includes the following constructors and global objects: - * - * DownloadsButton - * Main entry point for the downloads indicator. Depending on how the toolbars - * have been customized, this object determines if we should show a fully - * functional indicator, a placeholder used during customization and in the - * customization palette, or a neutral view as a temporary anchor for the - * downloads panel. - * - * DownloadsIndicatorView - * Builds and updates the actual downloads status widget, responding to changes - * in the global status data, or provides a neutral view if the indicator is - * removed from the toolbars and only used as a temporary anchor. In addition, - * handles the user interaction events raised by the widget. - */ - -"use strict"; - -//////////////////////////////////////////////////////////////////////////////// -//// DownloadsButton - -/** - * Main entry point for the downloads indicator. Depending on how the toolbars - * have been customized, this object determines if we should show a fully - * functional indicator, a placeholder used during customization and in the - * customization palette, or a neutral view as a temporary anchor for the - * downloads panel. - */ -const DownloadsButton = { - /** - * Location of the indicator overlay. - */ - get kIndicatorOverlay() - "chrome://browser/content/downloads/indicatorOverlay.xul", - - /** - * Returns a reference to the downloads button position placeholder, or null - * if not available because it has been removed from the toolbars. - */ - get _placeholder() - { - return document.getElementById("downloads-button"); - }, - - /** - * This function is called asynchronously just after window initialization. - * - * NOTE: This function should limit the input/output it performs to improve - * startup time, and in particular should not cause the Download Manager - * service to start. - */ - initializeIndicator: function DB_initializeIndicator() - { - this._update(); - }, - - /** - * Indicates whether toolbar customization is in progress. - */ - _customizing: false, - - /** - * This function is called when toolbar customization starts. - * - * During customization, we never show the actual download progress indication - * or the event notifications, but we show a neutral placeholder. The neutral - * placeholder is an ordinary button defined in the browser window that can be - * moved freely between the toolbars and the customization palette. - */ - customizeStart: function DB_customizeStart() - { - // Hide the indicator and prevent it to be displayed as a temporary anchor - // during customization, even if requested using the getAnchor method. - this._customizing = true; - this._anchorRequested = false; - - let indicator = DownloadsIndicatorView.indicator; - if (indicator) { - indicator.collapsed = true; - } - - let placeholder = this._placeholder; - if (placeholder) { - placeholder.collapsed = false; - } - }, - - /** - * This function is called when toolbar customization ends. - */ - customizeDone: function DB_customizeDone() - { - this._customizing = false; - this._update(); - }, - - /** - * This function is called during initialization or when toolbar customization - * ends. It determines if we should enable or disable the object that keeps - * the indicator updated, and ensures that the placeholder is hidden unless it - * has been moved to the customization palette. - * - * NOTE: This function is also called on startup, thus it should limit the - * input/output it performs, and in particular should not cause the - * Download Manager service to start. - */ - _update: function DB_update() { - this._updatePositionInternal(); - - if (!DownloadsCommon.useToolkitUI) { - DownloadsIndicatorView.ensureInitialized(); - } else { - DownloadsIndicatorView.ensureTerminated(); - } - }, - - /** - * Determines the position where the indicator should appear, and moves its - * associated element to the new position. This does not happen if the - * indicator is currently being used as the anchor for the panel, to ensure - * that the panel doesn't flicker because we move the DOM element to which - * it's anchored. - */ - updatePosition: function DB_updatePosition() - { - if (!this._anchorRequested) { - this._updatePositionInternal(); - } - }, - - /** - * Determines the position where the indicator should appear, and moves its - * associated element to the new position. - * - * @return Anchor element, or null if the indicator is not visible. - */ - _updatePositionInternal: function DB_updatePositionInternal() - { - let indicator = DownloadsIndicatorView.indicator; - if (!indicator) { - // Exit now if the indicator overlay isn't loaded yet. - return null; - } - - let placeholder = this._placeholder; - if (!placeholder) { - // The placeholder has been removed from the browser window. - indicator.collapsed = true; - // Move the indicator to a safe position on the toolbar, since otherwise - // it may break the merge of adjacent items, like back/forward + urlbar. - indicator.parentNode.appendChild(indicator); - return null; - } - - // Position the indicator where the placeholder is located. We should - // update the position even if the placeholder is located on an invisible - // toolbar, because the toolbar may be displayed later. - placeholder.parentNode.insertBefore(indicator, placeholder); - placeholder.collapsed = true; - indicator.collapsed = false; - - indicator.open = this._anchorRequested; - - // Determine if the placeholder is located on an invisible toolbar. - if (!isElementVisible(placeholder.parentNode)) { - return null; - } - - return DownloadsIndicatorView.indicatorAnchor; - }, - - /** - * Checks whether the indicator is, or will soon be visible in the browser - * window. - * - * @param aCallback - * Called once the indicator overlay has loaded. Gets a boolean - * argument representing the indicator visibility. - */ - checkIsVisible: function DB_checkIsVisible(aCallback) - { - function DB_CEV_callback() { - if (!this._placeholder) { - aCallback(false); - } else { - let element = DownloadsIndicatorView.indicator || this._placeholder; - aCallback(isElementVisible(element.parentNode)); - } - } - DownloadsOverlayLoader.ensureOverlayLoaded(this.kIndicatorOverlay, - DB_CEV_callback.bind(this)); - }, - - /** - * Indicates whether we should try and show the indicator temporarily as an - * anchor for the panel, even if the indicator would be hidden by default. - */ - _anchorRequested: false, - - /** - * Ensures that there is an anchor available for the panel. - * - * @param aCallback - * Called when the anchor is available, passing the element where the - * panel should be anchored, or null if an anchor is not available (for - * example because both the tab bar and the navigation bar are hidden). - */ - getAnchor: function DB_getAnchor(aCallback) - { - // Do not allow anchoring the panel to the element while customizing. - if (this._customizing) { - aCallback(null); - return; - } - - function DB_GA_callback() { - this._anchorRequested = true; - aCallback(this._updatePositionInternal()); - } - - DownloadsOverlayLoader.ensureOverlayLoaded(this.kIndicatorOverlay, - DB_GA_callback.bind(this)); - }, - - /** - * Allows the temporary anchor to be hidden. - */ - releaseAnchor: function DB_releaseAnchor() - { - this._anchorRequested = false; - this._updatePositionInternal(); - }, - - get _tabsToolbar() - { - delete this._tabsToolbar; - return this._tabsToolbar = document.getElementById("TabsToolbar"); - }, - - get _navBar() - { - delete this._navBar; - return this._navBar = document.getElementById("nav-bar"); - } -}; - -Object.defineProperty(this, "DownloadsButton", { - value: DownloadsButton, - enumerable: true, - writable: false -}); - -//////////////////////////////////////////////////////////////////////////////// -//// DownloadsIndicatorView - -/** - * Builds and updates the actual downloads status widget, responding to changes - * in the global status data, or provides a neutral view if the indicator is - * removed from the toolbars and only used as a temporary anchor. In addition, - * handles the user interaction events raised by the widget. - */ -const DownloadsIndicatorView = { - /** - * True when the view is connected with the underlying downloads data. - */ - _initialized: false, - - /** - * True when the user interface elements required to display the indicator - * have finished loading in the browser window, and can be referenced. - */ - _operational: false, - - /** - * Prepares the downloads indicator to be displayed. - */ - ensureInitialized: function DIV_ensureInitialized() - { - if (this._initialized) { - return; - } - this._initialized = true; - - window.addEventListener("unload", this.onWindowUnload, false); - DownloadsCommon.getIndicatorData(window).addView(this); - }, - - /** - * Frees the internal resources related to the indicator. - */ - ensureTerminated: function DIV_ensureTerminated() - { - if (!this._initialized) { - return; - } - this._initialized = false; - - window.removeEventListener("unload", this.onWindowUnload, false); - DownloadsCommon.getIndicatorData(window).removeView(this); - - // Reset the view properties, so that a neutral indicator is displayed if we - // are visible only temporarily as an anchor. - this.counter = ""; - this.percentComplete = 0; - this.paused = false; - this.attention = false; - }, - - /** - * Ensures that the user interface elements required to display the indicator - * are loaded, then invokes the given callback. - */ - _ensureOperational: function DIV_ensureOperational(aCallback) - { - if (this._operational) { - aCallback(); - return; - } - - function DIV_EO_callback() { - this._operational = true; - - // If the view is initialized, we need to update the elements now that - // they are finally available in the document. - if (this._initialized) { - DownloadsCommon.getIndicatorData(window).refreshView(this); - } - - aCallback(); - } - - DownloadsOverlayLoader.ensureOverlayLoaded( - DownloadsButton.kIndicatorOverlay, - DIV_EO_callback.bind(this)); - }, - - ////////////////////////////////////////////////////////////////////////////// - //// Direct control functions - - /** - * Set while we are waiting for a notification to fade out. - */ - _notificationTimeout: null, - - /** - * If the status indicator is visible in its assigned position, shows for a - * brief time a visual notification of a relevant event, like a new download. - * - * @param aType - * Set to "start" for new downloads, "finish" for completed downloads. - */ - showEventNotification: function DIV_showEventNotification(aType) - { - if (!this._initialized) { - return; - } - - if (!DownloadsCommon.animateNotifications) { - return; - } - - // No need to show visual notification if the panel is visible. - if (DownloadsPanel.isPanelShowing) { - return; - } - - function DIV_SEN_callback() { - if (this._notificationTimeout) { - clearTimeout(this._notificationTimeout); - } - - // Now that the overlay is loaded, place the indicator in its final - // position. - DownloadsButton.updatePosition(); - - let indicator = this.indicator; - indicator.setAttribute("notification", aType); - this._notificationTimeout = setTimeout( - function () indicator.removeAttribute("notification"), 1000); - } - - this._ensureOperational(DIV_SEN_callback.bind(this)); - }, - - ////////////////////////////////////////////////////////////////////////////// - //// Callback functions from DownloadsIndicatorData - - /** - * Indicates whether the indicator should be shown because there are some - * downloads to be displayed. - */ - set hasDownloads(aValue) - { - if (this._hasDownloads != aValue) { - this._hasDownloads = aValue; - - // If there is at least one download, ensure that the view elements are - // loaded before determining the position of the downloads button. - if (aValue) { - this._ensureOperational(function() DownloadsButton.updatePosition()); - } else { - DownloadsButton.updatePosition(); - } - } - return aValue; - }, - get hasDownloads() - { - return this._hasDownloads; - }, - _hasDownloads: false, - - /** - * Status text displayed in the indicator. If this is set to an empty value, - * then the small downloads icon is displayed instead of the text. - */ - set counter(aValue) - { - if (!this._operational) { - return this._counter; - } - - if (this._counter !== aValue) { - this._counter = aValue; - if (this._counter) - this.indicator.setAttribute("counter", "true"); - else - this.indicator.removeAttribute("counter"); - // We have to set the attribute instead of using the property because the - // XBL binding isn't applied if the element is invisible for any reason. - this._indicatorCounter.setAttribute("value", aValue); - } - return aValue; - }, - _counter: null, - - /** - * Progress indication to display, from 0 to 100, or -1 if unknown. The - * progress bar is hidden if the current progress is unknown and no status - * text is set in the "counter" property. - */ - set percentComplete(aValue) - { - if (!this._operational) { - return this._percentComplete; - } - - if (this._percentComplete !== aValue) { - this._percentComplete = aValue; - if (this._percentComplete >= 0) - this.indicator.setAttribute("progress", "true"); - else - this.indicator.removeAttribute("progress"); - // We have to set the attribute instead of using the property because the - // XBL binding isn't applied if the element is invisible for any reason. - this._indicatorProgress.setAttribute("value", Math.max(aValue, 0)); - } - return aValue; - }, - _percentComplete: null, - - /** - * Indicates whether the progress won't advance because of a paused state. - * Setting this property forces a paused progress bar to be displayed, even if - * the current progress information is unavailable. - */ - set paused(aValue) - { - if (!this._operational) { - return this._paused; - } - - if (this._paused != aValue) { - this._paused = aValue; - if (this._paused) { - this.indicator.setAttribute("paused", "true") - } else { - this.indicator.removeAttribute("paused"); - } - } - return aValue; - }, - _paused: false, - - /** - * Set when the indicator should draw user attention to itself. - */ - set attention(aValue) - { - if (!this._operational) { - return this._attention; - } - - if (this._attention != aValue) { - this._attention = aValue; - if (aValue) { - this.indicator.setAttribute("attention", "true"); - } else { - this.indicator.removeAttribute("attention"); - } - } - return aValue; - }, - _attention: false, - - ////////////////////////////////////////////////////////////////////////////// - //// User interface event functions - - onWindowUnload: function DIV_onWindowUnload() - { - // This function is registered as an event listener, we can't use "this". - DownloadsIndicatorView.ensureTerminated(); - }, - - onCommand: function DIV_onCommand(aEvent) - { - if (DownloadsCommon.useToolkitUI) { - // The panel won't suppress attention for us, we need to clear now. - DownloadsCommon.getIndicatorData(window).attention = false; - BrowserDownloadsUI(); - } else { - DownloadsPanel.showPanel(); - } - - aEvent.stopPropagation(); - }, - - onDragOver: function DIV_onDragOver(aEvent) - { - browserDragAndDrop.dragOver(aEvent); - }, - - onDrop: function DIV_onDrop(aEvent) - { - let dt = aEvent.dataTransfer; - // If dragged item is from our source, do not try to - // redownload already downloaded file. - if (dt.mozGetDataAt("application/x-moz-file", 0)) - return; - - let links = browserDragAndDrop.dropLinks(aEvent); - if (!links.length) - return; - let sourceDoc = dt.mozSourceNode ? dt.mozSourceNode.ownerDocument : document; - let handled = false; - for (let link of links) { - if (link.url.startsWith("about:")) - continue; - saveURL(link.url, link.name, null, true, true, null, sourceDoc); - handled = true; - } - if (handled) { - aEvent.preventDefault(); - } - }, - - /** - * Returns a reference to the main indicator element, or null if the element - * is not present in the browser window yet. - */ - get indicator() - { - let indicator = document.getElementById("downloads-indicator"); - if (!indicator) { - return null; - } - - // Once the element is loaded, it will never be unloaded. - delete this.indicator; - return this.indicator = indicator; - }, - - get indicatorAnchor() - { - delete this.indicatorAnchor; - return this.indicatorAnchor = - document.getElementById("downloads-indicator-anchor"); - }, - - get _indicatorCounter() - { - delete this._indicatorCounter; - return this._indicatorCounter = - document.getElementById("downloads-indicator-counter"); - }, - - get _indicatorProgress() - { - delete this._indicatorProgress; - return this._indicatorProgress = - document.getElementById("downloads-indicator-progress"); - } -}; - -Object.defineProperty(this, "DownloadsIndicatorView", { - value: DownloadsIndicatorView, - enumerable: true, - writable: false -}); diff --git a/components/downloads/content/indicatorOverlay.xul b/components/downloads/content/indicatorOverlay.xul deleted file mode 100644 index efb6cab..0000000 --- a/components/downloads/content/indicatorOverlay.xul +++ /dev/null @@ -1,60 +0,0 @@ -<?xml version="1.0"?> -<!-- -*- Mode: HTML; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- --> -<!-- vim: set ts=2 et sw=2 tw=80: --> - -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this file, - - You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<?xml-stylesheet href="chrome://browser/content/downloads/downloads.css"?> -<?xml-stylesheet href="chrome://browser/skin/downloads/downloads.css"?> - -<!DOCTYPE overlay [ - <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd" > - %browserDTD; - <!ENTITY % downloadsDTD SYSTEM "chrome://browser/locale/downloads/downloads.dtd" > - %downloadsDTD; -]> - -<overlay xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - id="indicatorOverlay"> - - <popupset> - <!-- The downloads indicator is placed in its final toolbar location - programmatically, and can be shown temporarily even when its - placeholder is removed from the toolbars. Its initial location within - the document must not be a toolbar or the toolbar palette, otherwise the - toolbar handling code could remove it from the document. --> - <toolbarbutton id="downloads-indicator" - class="toolbarbutton-1 chromeclass-toolbar-additional" - tooltiptext="&downloads.tooltip;" - collapsed="true" - oncommand="DownloadsIndicatorView.onCommand(event);" - ondrop="DownloadsIndicatorView.onDrop(event);" - ondragover="DownloadsIndicatorView.onDragOver(event);" - ondragenter="DownloadsIndicatorView.onDragOver(event);" - ondragleave="DownloadsIndicatorView.onDragLeave(event);" - skipintoolbarset="true"> - <!-- The panel's anchor area is smaller than the outer button, but must - always be visible and must not move or resize when the indicator - state changes, otherwise the panel could change its position or lose - its arrow unexpectedly. --> - <stack id="downloads-indicator-anchor" - class="toolbarbutton-icon"> - <vbox id="downloads-indicator-progress-area" - pack="center"> - <description id="downloads-indicator-counter"/> - <progressmeter id="downloads-indicator-progress" - class="plain" - min="0" - max="100"/> - </vbox> - <vbox id="downloads-indicator-icon"/> - <vbox id="downloads-indicator-notification"/> - </stack> - <label class="toolbarbutton-text" crop="right" flex="1" - value="&downloads.label;"/> - </toolbarbutton> - </popupset> -</overlay> diff --git a/components/downloads/jar.mn b/components/downloads/jar.mn deleted file mode 100644 index 8c0b519..0000000 --- a/components/downloads/jar.mn +++ /dev/null @@ -1,18 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -browser.jar: -* content/browser/downloads/download.xml (content/download.xml) - content/browser/downloads/download.css (content/download.css) - content/browser/downloads/downloads.css (content/downloads.css) -* content/browser/downloads/downloads.js (content/downloads.js) -* content/browser/downloads/downloadsOverlay.xul (content/downloadsOverlay.xul) - content/browser/downloads/indicator.js (content/indicator.js) - content/browser/downloads/indicatorOverlay.xul (content/indicatorOverlay.xul) -* content/browser/downloads/allDownloadsViewOverlay.xul (content/allDownloadsViewOverlay.xul) - content/browser/downloads/allDownloadsViewOverlay.js (content/allDownloadsViewOverlay.js) - content/browser/downloads/allDownloadsViewOverlay.css (content/allDownloadsViewOverlay.css) -* content/browser/downloads/contentAreaDownloadsView.xul (content/contentAreaDownloadsView.xul) - content/browser/downloads/contentAreaDownloadsView.js (content/contentAreaDownloadsView.js) - content/browser/downloads/contentAreaDownloadsView.css (content/contentAreaDownloadsView.css) diff --git a/components/downloads/moz.build b/components/downloads/moz.build deleted file mode 100644 index abfaab7..0000000 --- a/components/downloads/moz.build +++ /dev/null @@ -1,23 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -JAR_MANIFESTS += ['jar.mn'] - -EXTRA_COMPONENTS += [ - 'BrowserDownloads.manifest', - 'DownloadsStartup.js', - 'DownloadsUI.js', -] - -EXTRA_JS_MODULES += [ - 'DownloadsLogger.jsm', - 'DownloadsTaskbar.jsm', - 'DownloadsViewUI.jsm', -] - -EXTRA_PP_JS_MODULES += [ - 'DownloadsCommon.jsm', -] diff --git a/components/feeds/BrowserFeeds.manifest b/components/feeds/BrowserFeeds.manifest deleted file mode 100644 index a584323..0000000 --- a/components/feeds/BrowserFeeds.manifest +++ /dev/null @@ -1,28 +0,0 @@ -# WebappRT doesn't need these instructions, and they don't necessarily work -# with it, but it does use a GRE directory that the GRE shares with Firefox, -# so in order to prevent the instructions from being processed for WebappRT, -# we need to restrict them to the applications that depend on them, i.e.: -# -# b2g: {3c2e2abc-06d4-11e1-ac3b-374f68613e61} -# browser: {8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -# mobile/android: {aa3c5121-dab2-40e2-81ca-7ea25febc110} -# mobile/xul: {a23983c0-fd0e-11dc-95ff-0800200c9a66} -# -# In theory we should do this for all these instructions, but in practice it is -# sufficient to do it for the app-startup one, and the file is simpler that way. - -component {229fa115-9412-4d32-baf3-2fc407f76fb1} FeedConverter.js -contract @mozilla.org/streamconv;1?from=application/vnd.mozilla.maybe.feed&to=*/* {229fa115-9412-4d32-baf3-2fc407f76fb1} -contract @mozilla.org/streamconv;1?from=application/vnd.mozilla.maybe.video.feed&to=*/* {229fa115-9412-4d32-baf3-2fc407f76fb1} -contract @mozilla.org/streamconv;1?from=application/vnd.mozilla.maybe.audio.feed&to=*/* {229fa115-9412-4d32-baf3-2fc407f76fb1} -component {2376201c-bbc6-472f-9b62-7548040a61c6} FeedConverter.js -contract @mozilla.org/browser/feeds/result-service;1 {2376201c-bbc6-472f-9b62-7548040a61c6} -component {4f91ef2e-57ba-472e-ab7a-b4999e42d6c0} FeedConverter.js -contract @mozilla.org/network/protocol;1?name=feed {4f91ef2e-57ba-472e-ab7a-b4999e42d6c0} -component {1c31ed79-accd-4b94-b517-06e0c81999d5} FeedConverter.js -contract @mozilla.org/network/protocol;1?name=pcast {1c31ed79-accd-4b94-b517-06e0c81999d5} -component {49bb6593-3aff-4eb3-a068-2712c28bd58e} FeedWriter.js -contract @mozilla.org/browser/feeds/result-writer;1 {49bb6593-3aff-4eb3-a068-2712c28bd58e} -component {792a7e82-06a0-437c-af63-b2d12e808acc} WebContentConverter.js -contract @mozilla.org/embeddor.implemented/web-content-handler-registrar;1 {792a7e82-06a0-437c-af63-b2d12e808acc} -category app-startup WebContentConverter service,@mozilla.org/embeddor.implemented/web-content-handler-registrar;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66} diff --git a/components/feeds/FeedConverter.js b/components/feeds/FeedConverter.js deleted file mode 100644 index d0f5737..0000000 --- a/components/feeds/FeedConverter.js +++ /dev/null @@ -1,591 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); -Components.utils.import("resource://gre/modules/debug.js"); -Components.utils.import("resource://gre/modules/Services.jsm"); - -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cr = Components.results; - -function LOG(str) { - dump("*** " + str + "\n"); -} - -const FS_CONTRACTID = "@mozilla.org/browser/feeds/result-service;1"; -const FPH_CONTRACTID = "@mozilla.org/network/protocol;1?name=feed"; -const PCPH_CONTRACTID = "@mozilla.org/network/protocol;1?name=pcast"; - -const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed"; -const TYPE_MAYBE_VIDEO_FEED = "application/vnd.mozilla.maybe.video.feed"; -const TYPE_MAYBE_AUDIO_FEED = "application/vnd.mozilla.maybe.audio.feed"; -const TYPE_ANY = "*/*"; - -const PREF_SELECTED_APP = "browser.feeds.handlers.application"; -const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice"; -const PREF_SELECTED_ACTION = "browser.feeds.handler"; -const PREF_SELECTED_READER = "browser.feeds.handler.default"; - -const PREF_VIDEO_SELECTED_APP = "browser.videoFeeds.handlers.application"; -const PREF_VIDEO_SELECTED_WEB = "browser.videoFeeds.handlers.webservice"; -const PREF_VIDEO_SELECTED_ACTION = "browser.videoFeeds.handler"; -const PREF_VIDEO_SELECTED_READER = "browser.videoFeeds.handler.default"; - -const PREF_AUDIO_SELECTED_APP = "browser.audioFeeds.handlers.application"; -const PREF_AUDIO_SELECTED_WEB = "browser.audioFeeds.handlers.webservice"; -const PREF_AUDIO_SELECTED_ACTION = "browser.audioFeeds.handler"; -const PREF_AUDIO_SELECTED_READER = "browser.audioFeeds.handler.default"; - -function getPrefAppForType(t) { - switch (t) { - case Ci.nsIFeed.TYPE_VIDEO: - return PREF_VIDEO_SELECTED_APP; - - case Ci.nsIFeed.TYPE_AUDIO: - return PREF_AUDIO_SELECTED_APP; - - default: - return PREF_SELECTED_APP; - } -} - -function getPrefWebForType(t) { - switch (t) { - case Ci.nsIFeed.TYPE_VIDEO: - return PREF_VIDEO_SELECTED_WEB; - - case Ci.nsIFeed.TYPE_AUDIO: - return PREF_AUDIO_SELECTED_WEB; - - default: - return PREF_SELECTED_WEB; - } -} - -function getPrefActionForType(t) { - switch (t) { - case Ci.nsIFeed.TYPE_VIDEO: - return PREF_VIDEO_SELECTED_ACTION; - - case Ci.nsIFeed.TYPE_AUDIO: - return PREF_AUDIO_SELECTED_ACTION; - - default: - return PREF_SELECTED_ACTION; - } -} - -function getPrefReaderForType(t) { - switch (t) { - case Ci.nsIFeed.TYPE_VIDEO: - return PREF_VIDEO_SELECTED_READER; - - case Ci.nsIFeed.TYPE_AUDIO: - return PREF_AUDIO_SELECTED_READER; - - default: - return PREF_SELECTED_READER; - } -} - -function safeGetCharPref(pref, defaultValue) { - var prefs = - Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefBranch); - try { - return prefs.getCharPref(pref); - } - catch (e) { - } - return defaultValue; -} - -function FeedConverter() { -} -FeedConverter.prototype = { - classID: Components.ID("{229fa115-9412-4d32-baf3-2fc407f76fb1}"), - - /** - * This is the downloaded text data for the feed. - */ - _data: null, - - /** - * This is the object listening to the conversion, which is ultimately the - * docshell for the load. - */ - _listener: null, - - /** - * Records if the feed was sniffed - */ - _sniffed: false, - - /** - * See nsIStreamConverter.idl - */ - convert: function FC_convert(sourceStream, sourceType, destinationType, - context) { - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - }, - - /** - * See nsIStreamConverter.idl - */ - asyncConvertData: function FC_asyncConvertData(sourceType, destinationType, - listener, context) { - this._listener = listener; - }, - - /** - * Whether or not the preview page is being forced. - */ - _forcePreviewPage: false, - - /** - * Release our references to various things once we're done using them. - */ - _releaseHandles: function FC__releaseHandles() { - this._listener = null; - this._request = null; - this._processor = null; - }, - - /** - * See nsIFeedResultListener.idl - */ - handleResult: function FC_handleResult(result) { - // Feeds come in various content types, which our feed sniffer coerces to - // the maybe.feed type. However, feeds are used as a transport for - // different data types, e.g. news/blogs (traditional feed), video/audio - // (podcasts) and photos (photocasts, photostreams). Each of these is - // different in that there's a different class of application suitable for - // handling feeds of that type, but without a content-type differentiation - // it is difficult for us to disambiguate. - // - // The other problem is that if the user specifies an auto-action handler - // for one feed application, the fact that the content type is shared means - // that all other applications will auto-load with that handler too, - // regardless of the content-type. - // - // This means that content-type alone is not enough to determine whether - // or not a feed should be auto-handled. This means that for feeds we need - // to always use this stream converter, even when an auto-action is - // specified, not the basic one provided by WebContentConverter. This - // converter needs to consume all of the data and parse it, and based on - // that determination make a judgment about type. - // - // Since there are no content types for this content, and I'm not going to - // invent any, the upshot is that while a user can set an auto-handler for - // generic feed content, the system will prevent them from setting an auto- - // handler for other stream types. In those cases, the user will always see - // the preview page and have to select a handler. We can guess and show - // a client handler, but will not be able to show web handlers for those - // types. - // - // If this is just a feed, not some kind of specialized application, then - // auto-handlers can be set and we should obey them. - try { - var feedService = - Cc["@mozilla.org/browser/feeds/result-service;1"]. - getService(Ci.nsIFeedResultService); - if (!this._forcePreviewPage && result.doc) { - var feed = result.doc.QueryInterface(Ci.nsIFeed); - var handler = safeGetCharPref(getPrefActionForType(feed.type), "ask"); - - if (handler != "ask") { - if (handler == "reader") - handler = safeGetCharPref(getPrefReaderForType(feed.type), "bookmarks"); - switch (handler) { - case "web": - var wccr = - Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"]. - getService(Ci.nsIWebContentConverterService); - if ((feed.type == Ci.nsIFeed.TYPE_FEED && - wccr.getAutoHandler(TYPE_MAYBE_FEED)) || - (feed.type == Ci.nsIFeed.TYPE_VIDEO && - wccr.getAutoHandler(TYPE_MAYBE_VIDEO_FEED)) || - (feed.type == Ci.nsIFeed.TYPE_AUDIO && - wccr.getAutoHandler(TYPE_MAYBE_AUDIO_FEED))) { - wccr.loadPreferredHandler(this._request); - return; - } - break; - - default: - LOG("unexpected handler: " + handler); - // fall through -- let feed service handle error - case "bookmarks": - case "client": - try { - var title = feed.title ? feed.title.plainText() : ""; - var desc = feed.subtitle ? feed.subtitle.plainText() : ""; - feedService.addToClientReader(result.uri.spec, title, desc, feed.type); - return; - } catch(ex) { /* fallback to preview mode */ } - } - } - } - - var ios = - Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - var chromeChannel; - - // handling a redirect, hence forwarding the loadInfo from the old channel - // to the newchannel. - var oldChannel = this._request.QueryInterface(Ci.nsIChannel); - var loadInfo = oldChannel.loadInfo; - - // If there was no automatic handler, or this was a podcast, - // photostream or some other kind of application, show the preview page - // if the parser returned a document. - if (result.doc) { - - // Store the result in the result service so that the display - // page can access it. - feedService.addFeedResult(result); - - // Now load the actual XUL document. - var aboutFeedsURI = ios.newURI("about:feeds", null, null); - chromeChannel = ios.newChannelFromURIWithLoadInfo(aboutFeedsURI, loadInfo); - chromeChannel.originalURI = result.uri; - chromeChannel.owner = - Services.scriptSecurityManager.getNoAppCodebasePrincipal(aboutFeedsURI); - } else { - chromeChannel = ios.newChannelFromURIWithLoadInfo(result.uri, loadInfo); - } - - chromeChannel.loadGroup = this._request.loadGroup; - chromeChannel.asyncOpen2(this._listener); - } - finally { - this._releaseHandles(); - } - }, - - /** - * See nsIStreamListener.idl - */ - onDataAvailable: function FC_onDataAvailable(request, context, inputStream, - sourceOffset, count) { - if (this._processor) - this._processor.onDataAvailable(request, context, inputStream, - sourceOffset, count); - }, - - /** - * See nsIRequestObserver.idl - */ - onStartRequest: function FC_onStartRequest(request, context) { - var channel = request.QueryInterface(Ci.nsIChannel); - - // Check for a header that tells us there was no sniffing - // The value doesn't matter. - try { - var httpChannel = channel.QueryInterface(Ci.nsIHttpChannel); - // Make sure to check requestSucceeded before the potentially-throwing - // getResponseHeader. - if (!httpChannel.requestSucceeded) { - // Just give up, but don't forget to cancel the channel first! - request.cancel(Cr.NS_BINDING_ABORTED); - return; - } - var noSniff = httpChannel.getResponseHeader("X-Moz-Is-Feed"); - } - catch (ex) { - this._sniffed = true; - } - - this._request = request; - - // Save and reset the forced state bit early, in case there's some kind of - // error. - var feedService = - Cc["@mozilla.org/browser/feeds/result-service;1"]. - getService(Ci.nsIFeedResultService); - this._forcePreviewPage = feedService.forcePreviewPage; - feedService.forcePreviewPage = false; - - // Parse feed data as it comes in - this._processor = - Cc["@mozilla.org/feed-processor;1"]. - createInstance(Ci.nsIFeedProcessor); - this._processor.listener = this; - this._processor.parseAsync(null, channel.URI); - - this._processor.onStartRequest(request, context); - }, - - /** - * See nsIRequestObserver.idl - */ - onStopRequest: function FC_onStopRequest(request, context, status) { - if (this._processor) - this._processor.onStopRequest(request, context, status); - }, - - /** - * See nsISupports.idl - */ - QueryInterface: function FC_QueryInterface(iid) { - if (iid.equals(Ci.nsIFeedResultListener) || - iid.equals(Ci.nsIStreamConverter) || - iid.equals(Ci.nsIStreamListener) || - iid.equals(Ci.nsIRequestObserver)|| - iid.equals(Ci.nsISupports)) - return this; - throw Cr.NS_ERROR_NO_INTERFACE; - }, -}; - -/** - * Keeps parsed FeedResults around for use elsewhere in the UI after the stream - * converter completes. - */ -function FeedResultService() { -} - -FeedResultService.prototype = { - classID: Components.ID("{2376201c-bbc6-472f-9b62-7548040a61c6}"), - - /** - * A URI spec -> [nsIFeedResult] hash. We have to keep a list as the - * value in case the same URI is requested concurrently. - */ - _results: { }, - - /** - * See nsIFeedResultService.idl - */ - forcePreviewPage: false, - - /** - * See nsIFeedResultService.idl - */ - addToClientReader: function FRS_addToClientReader(spec, title, subtitle, feedType) { - var prefs = - Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefBranch); - - var handler = safeGetCharPref(getPrefActionForType(feedType), "bookmarks"); - if (handler == "ask" || handler == "reader") - handler = safeGetCharPref(getPrefReaderForType(feedType), "bookmarks"); - - switch (handler) { - case "client": - var clientApp = prefs.getComplexValue(getPrefAppForType(feedType), Ci.nsILocalFile); - - // For the benefit of applications that might know how to deal with more - // URLs than just feeds, send feed: URLs in the following format: - // - // http urls: replace scheme with feed, e.g. - // http://foo.com/index.rdf -> feed://foo.com/index.rdf - // other urls: prepend feed: scheme, e.g. - // https://foo.com/index.rdf -> feed:https://foo.com/index.rdf - var ios = - Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - var feedURI = ios.newURI(spec, null, null); - if (feedURI.schemeIs("http")) { - feedURI.scheme = "feed"; - spec = feedURI.spec; - } - else - spec = "feed:" + spec; - - // Retrieving the shell service might fail on some systems, most - // notably systems where GNOME is not installed. - try { - var ss = - Cc["@mozilla.org/browser/shell-service;1"]. - getService(Ci.nsIShellService); - ss.openApplicationWithURI(clientApp, spec); - } catch(e) { - // If we couldn't use the shell service, fallback to using a - // nsIProcess instance - var p = - Cc["@mozilla.org/process/util;1"]. - createInstance(Ci.nsIProcess); - p.init(clientApp); - p.run(false, [spec], 1); - } - break; - - default: - // "web" should have been handled elsewhere - LOG("unexpected handler: " + handler); - // fall through - case "bookmarks": - var wm = - Cc["@mozilla.org/appshell/window-mediator;1"]. - getService(Ci.nsIWindowMediator); - var topWindow = wm.getMostRecentWindow("navigator:browser"); - topWindow.PlacesCommandHook.addLiveBookmark(spec, title, subtitle); - break; - } - }, - - /** - * See nsIFeedResultService.idl - */ - addFeedResult: function FRS_addFeedResult(feedResult) { - NS_ASSERT(feedResult.uri != null, "null URI!"); - NS_ASSERT(feedResult.uri != null, "null feedResult!"); - var spec = feedResult.uri.spec; - if(!this._results[spec]) - this._results[spec] = []; - this._results[spec].push(feedResult); - }, - - /** - * See nsIFeedResultService.idl - */ - getFeedResult: function RFS_getFeedResult(uri) { - NS_ASSERT(uri != null, "null URI!"); - var resultList = this._results[uri.spec]; - for (var i in resultList) { - if (resultList[i].uri == uri) - return resultList[i]; - } - return null; - }, - - /** - * See nsIFeedResultService.idl - */ - removeFeedResult: function FRS_removeFeedResult(uri) { - NS_ASSERT(uri != null, "null URI!"); - var resultList = this._results[uri.spec]; - if (!resultList) - return; - var deletions = 0; - for (var i = 0; i < resultList.length; ++i) { - if (resultList[i].uri == uri) { - delete resultList[i]; - ++deletions; - } - } - - // send the holes to the end - resultList.sort(); - // and trim the list - resultList.splice(resultList.length - deletions, deletions); - if (resultList.length == 0) - delete this._results[uri.spec]; - }, - - createInstance: function FRS_createInstance(outer, iid) { - if (outer != null) - throw Cr.NS_ERROR_NO_AGGREGATION; - return this.QueryInterface(iid); - }, - - QueryInterface: function FRS_QueryInterface(iid) { - if (iid.equals(Ci.nsIFeedResultService) || - iid.equals(Ci.nsIFactory) || - iid.equals(Ci.nsISupports)) - return this; - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - }, -}; - -/** - * A protocol handler that attempts to deal with the variant forms of feed: - * URIs that are actually either http or https. - */ -function GenericProtocolHandler() { -} -GenericProtocolHandler.prototype = { - _init: function GPH_init(scheme) { - var ios = - Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - this._http = ios.getProtocolHandler("http"); - this._scheme = scheme; - }, - - get scheme() { - return this._scheme; - }, - - get protocolFlags() { - return this._http.protocolFlags; - }, - - get defaultPort() { - return this._http.defaultPort; - }, - - allowPort: function GPH_allowPort(port, scheme) { - return this._http.allowPort(port, scheme); - }, - - newURI: function GPH_newURI(spec, originalCharset, baseURI) { - // Feed URIs can be either nested URIs of the form feed:realURI (in which - // case we create a nested URI for the realURI) or feed://example.com, in - // which case we create a nested URI for the real protocol which is http. - - var scheme = this._scheme + ":"; - if (spec.substr(0, scheme.length) != scheme) - throw Cr.NS_ERROR_MALFORMED_URI; - - var prefix = spec.substr(scheme.length, 2) == "//" ? "http:" : ""; - var inner = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService).newURI(spec.replace(scheme, prefix), - originalCharset, baseURI); - var netutil = Cc["@mozilla.org/network/util;1"].getService(Ci.nsINetUtil); - const URI_INHERITS_SECURITY_CONTEXT = Ci.nsIProtocolHandler - .URI_INHERITS_SECURITY_CONTEXT; - if (netutil.URIChainHasFlags(inner, URI_INHERITS_SECURITY_CONTEXT)) - throw Cr.NS_ERROR_MALFORMED_URI; - - var uri = netutil.newSimpleNestedURI(inner); - uri.spec = inner.spec.replace(prefix, scheme); - return uri; - }, - - newChannel2: function GPH_newChannel(aUri, aLoadInfo) { - var inner = aUri.QueryInterface(Ci.nsINestedURI).innerURI; - var channel = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService). - newChannelFromURIWithLoadInfo(inner, aLoadInfo); - - if (channel instanceof Components.interfaces.nsIHttpChannel) - // Set this so we know this is supposed to be a feed - channel.setRequestHeader("X-Moz-Is-Feed", "1", false); - channel.originalURI = aUri; - return channel; - }, - - - QueryInterface: function GPH_QueryInterface(iid) { - if (iid.equals(Ci.nsIProtocolHandler) || - iid.equals(Ci.nsISupports)) - return this; - throw Cr.NS_ERROR_NO_INTERFACE; - } -}; - -function FeedProtocolHandler() { - this._init('feed'); -} -FeedProtocolHandler.prototype = new GenericProtocolHandler(); -FeedProtocolHandler.prototype.classID = Components.ID("{4f91ef2e-57ba-472e-ab7a-b4999e42d6c0}"); - -function PodCastProtocolHandler() { - this._init('pcast'); -} -PodCastProtocolHandler.prototype = new GenericProtocolHandler(); -PodCastProtocolHandler.prototype.classID = Components.ID("{1c31ed79-accd-4b94-b517-06e0c81999d5}"); - -var components = [FeedConverter, - FeedResultService, - FeedProtocolHandler, - PodCastProtocolHandler]; - - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components); diff --git a/components/feeds/FeedWriter.js b/components/feeds/FeedWriter.js deleted file mode 100644 index facde58..0000000 --- a/components/feeds/FeedWriter.js +++ /dev/null @@ -1,1397 +0,0 @@ -# -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cr = Components.results; -const Cu = Components.utils; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/NetUtil.jsm"); - -const FEEDWRITER_CID = Components.ID("{49bb6593-3aff-4eb3-a068-2712c28bd58e}"); -const FEEDWRITER_CONTRACTID = "@mozilla.org/browser/feeds/result-writer;1"; - -function LOG(str) { - var prefB = Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefBranch); - - var shouldLog = prefB.getBoolPref("feeds.log", false); - - if (shouldLog) - dump("*** Feeds: " + str + "\n"); -} - -/** - * Wrapper function for nsIIOService::newURI. - * @param aURLSpec - * The URL string from which to create an nsIURI. - * @returns an nsIURI object, or null if the creation of the URI failed. - */ -function makeURI(aURLSpec, aCharset) { - var ios = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - try { - return ios.newURI(aURLSpec, aCharset, null); - } catch (ex) { } - - return null; -} - -const XML_NS = "http://www.w3.org/XML/1998/namespace"; -const HTML_NS = "http://www.w3.org/1999/xhtml"; -const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; -const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed"; -const TYPE_MAYBE_AUDIO_FEED = "application/vnd.mozilla.maybe.audio.feed"; -const TYPE_MAYBE_VIDEO_FEED = "application/vnd.mozilla.maybe.video.feed"; -const URI_BUNDLE = "chrome://browser/locale/feeds/subscribe.properties"; - -const PREF_SELECTED_APP = "browser.feeds.handlers.application"; -const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice"; -const PREF_SELECTED_ACTION = "browser.feeds.handler"; -const PREF_SELECTED_READER = "browser.feeds.handler.default"; - -const PREF_VIDEO_SELECTED_APP = "browser.videoFeeds.handlers.application"; -const PREF_VIDEO_SELECTED_WEB = "browser.videoFeeds.handlers.webservice"; -const PREF_VIDEO_SELECTED_ACTION = "browser.videoFeeds.handler"; -const PREF_VIDEO_SELECTED_READER = "browser.videoFeeds.handler.default"; - -const PREF_AUDIO_SELECTED_APP = "browser.audioFeeds.handlers.application"; -const PREF_AUDIO_SELECTED_WEB = "browser.audioFeeds.handlers.webservice"; -const PREF_AUDIO_SELECTED_ACTION = "browser.audioFeeds.handler"; -const PREF_AUDIO_SELECTED_READER = "browser.audioFeeds.handler.default"; - -const PREF_SHOW_FIRST_RUN_UI = "browser.feeds.showFirstRunUI"; - -const TITLE_ID = "feedTitleText"; -const SUBTITLE_ID = "feedSubtitleText"; - -function getPrefAppForType(t) { - switch (t) { - case Ci.nsIFeed.TYPE_VIDEO: - return PREF_VIDEO_SELECTED_APP; - - case Ci.nsIFeed.TYPE_AUDIO: - return PREF_AUDIO_SELECTED_APP; - - default: - return PREF_SELECTED_APP; - } -} - -function getPrefWebForType(t) { - switch (t) { - case Ci.nsIFeed.TYPE_VIDEO: - return PREF_VIDEO_SELECTED_WEB; - - case Ci.nsIFeed.TYPE_AUDIO: - return PREF_AUDIO_SELECTED_WEB; - - default: - return PREF_SELECTED_WEB; - } -} - -function getPrefActionForType(t) { - switch (t) { - case Ci.nsIFeed.TYPE_VIDEO: - return PREF_VIDEO_SELECTED_ACTION; - - case Ci.nsIFeed.TYPE_AUDIO: - return PREF_AUDIO_SELECTED_ACTION; - - default: - return PREF_SELECTED_ACTION; - } -} - -function getPrefReaderForType(t) { - switch (t) { - case Ci.nsIFeed.TYPE_VIDEO: - return PREF_VIDEO_SELECTED_READER; - - case Ci.nsIFeed.TYPE_AUDIO: - return PREF_AUDIO_SELECTED_READER; - - default: - return PREF_SELECTED_READER; - } -} - -/** - * Converts a number of bytes to the appropriate unit that results in a - * number that needs fewer than 4 digits - * - * @return a pair: [new value with 3 sig. figs., its unit] - */ -function convertByteUnits(aBytes) { - var units = ["bytes", "kilobyte", "megabyte", "gigabyte"]; - let unitIndex = 0; - - // convert to next unit if it needs 4 digits (after rounding), but only if - // we know the name of the next unit - while ((aBytes >= 999.5) && (unitIndex < units.length - 1)) { - aBytes /= 1024; - unitIndex++; - } - - // Get rid of insignificant bits by truncating to 1 or 0 decimal points - // 0 -> 0; 1.2 -> 1.2; 12.3 -> 12.3; 123.4 -> 123; 234.5 -> 235 - aBytes = aBytes.toFixed((aBytes > 0) && (aBytes < 100) ? 1 : 0); - - return [aBytes, units[unitIndex]]; -} - -function FeedWriter() {} -FeedWriter.prototype = { - _mimeSvc : Cc["@mozilla.org/mime;1"]. - getService(Ci.nsIMIMEService), - - _getPropertyAsBag: function FW__getPropertyAsBag(container, property) { - return container.fields.getProperty(property). - QueryInterface(Ci.nsIPropertyBag2); - }, - - _getPropertyAsString: function FW__getPropertyAsString(container, property) { - try { - return container.fields.getPropertyAsAString(property); - } - catch (e) { - } - return ""; - }, - - _setContentText: function FW__setContentText(id, text) { - this._contentSandbox.element = this._document.getElementById(id); - this._contentSandbox.textNode = text.createDocumentFragment(this._contentSandbox.element); - var codeStr = - "while (element.hasChildNodes()) " + - " element.removeChild(element.firstChild);" + - "element.appendChild(textNode);"; - if (text.base) { - this._contentSandbox.spec = text.base.spec; - codeStr += "element.setAttributeNS('" + XML_NS + "', 'base', spec);"; - } - Cu.evalInSandbox(codeStr, this._contentSandbox); - this._contentSandbox.element = null; - this._contentSandbox.textNode = null; - }, - - /** - * Safely sets the href attribute on an anchor tag, providing the URI - * specified can be loaded according to rules. - * @param element - * The element to set a URI attribute on - * @param attribute - * The attribute of the element to set the URI to, e.g. href or src - * @param uri - * The URI spec to set as the href - */ - _safeSetURIAttribute: - function FW__safeSetURIAttribute(element, attribute, uri) { - var secman = Cc["@mozilla.org/scriptsecuritymanager;1"]. - getService(Ci.nsIScriptSecurityManager); - const flags = Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL; - try { - secman.checkLoadURIStrWithPrincipal(this._feedPrincipal, uri, flags); - // checkLoadURIStrWithPrincipal will throw if the link URI should not be - // loaded, either because our feedURI isn't allowed to load it or per - // the rules specified in |flags|, so we'll never "linkify" the link... - } - catch (e) { - // Not allowed to load this link because secman.checkLoadURIStr threw - return; - } - - this._contentSandbox.element = element; - this._contentSandbox.uri = uri; - var codeStr = "element.setAttribute('" + attribute + "', uri);"; - Cu.evalInSandbox(codeStr, this._contentSandbox); - }, - - /** - * Use this sandbox to run any dom manipulation code on nodes which - * are already inserted into the content document. - */ - __contentSandbox: null, - get _contentSandbox() { - // This whole sandbox setup is totally archaic. It was introduced in bug - // 360529, presumably before the existence of a solid security membrane, - // since all of the manipulation of content here should be made safe by - // Xrays. - // Now that anonymous content is no longer content-accessible, manipulating - // the xml stylesheet content can't be done from content anymore. - // - // The right solution would be to rip out all of this sandbox junk and - // manipulate the DOM directly, but that would require a lot of rewriting. - // So, for now, we just give the sandbox an nsExpandedPrincipal with []. - // This has the effect of giving it Xrays, and making it same-origin with - // the XBL scope, thereby letting it manipulate anonymous content. - if (!this.__contentSandbox) - this.__contentSandbox = new Cu.Sandbox([this._window], - {sandboxName: 'FeedWriter'}); - - return this.__contentSandbox; - }, - - /** - * Calls doCommand for a given XUL element within the context of the - * content document. - * - * @param aElement - * the XUL element to call doCommand() on. - */ - _safeDoCommand: function FW___safeDoCommand(aElement) { - this._contentSandbox.element = aElement; - Cu.evalInSandbox("element.doCommand();", this._contentSandbox); - this._contentSandbox.element = null; - }, - - __faviconService: null, - get _faviconService() { - if (!this.__faviconService) - this.__faviconService = Cc["@mozilla.org/browser/favicon-service;1"]. - getService(Ci.nsIFaviconService); - - return this.__faviconService; - }, - - __bundle: null, - get _bundle() { - if (!this.__bundle) { - this.__bundle = Cc["@mozilla.org/intl/stringbundle;1"]. - getService(Ci.nsIStringBundleService). - createBundle(URI_BUNDLE); - } - return this.__bundle; - }, - - _getFormattedString: function FW__getFormattedString(key, params) { - return this._bundle.formatStringFromName(key, params, params.length); - }, - - _getString: function FW__getString(key) { - return this._bundle.GetStringFromName(key); - }, - - /* Magic helper methods to be used instead of xbl properties */ - _getSelectedItemFromMenulist: function FW__getSelectedItemFromList(aList) { - var node = aList.firstChild.firstChild; - while (node) { - if (node.localName == "menuitem" && node.getAttribute("selected") == "true") - return node; - - node = node.nextSibling; - } - - return null; - }, - - _setCheckboxCheckedState: function FW__setCheckboxCheckedState(aCheckbox, aValue) { - // see checkbox.xml, xbl bindings are not applied within the sandbox! - this._contentSandbox.checkbox = aCheckbox; - var codeStr; - var change = (aValue != (aCheckbox.getAttribute('checked') == 'true')); - if (aValue) - codeStr = "checkbox.setAttribute('checked', 'true'); "; - else - codeStr = "checkbox.removeAttribute('checked'); "; - - if (change) { - this._contentSandbox.document = this._document; - codeStr += "var event = document.createEvent('Events'); " + - "event.initEvent('CheckboxStateChange', true, true);" + - "checkbox.dispatchEvent(event);" - } - - Cu.evalInSandbox(codeStr, this._contentSandbox); - }, - - /** - * Returns a date suitable for displaying in the feed preview. - * If the date cannot be parsed, the return value is "false". - * @param dateString - * A date as extracted from a feed entry. (entry.updated) - */ - _parseDate: function FW__parseDate(dateString) { - // Convert the date into the user's local time zone - dateObj = new Date(dateString); - - // Make sure the date we're given is valid. - if (!dateObj.getTime()) - return false; - - var dateService = Cc["@mozilla.org/intl/scriptabledateformat;1"]. - getService(Ci.nsIScriptableDateFormat); - return dateService.FormatDateTime("", dateService.dateFormatLong, dateService.timeFormatNoSeconds, - dateObj.getFullYear(), dateObj.getMonth()+1, dateObj.getDate(), - dateObj.getHours(), dateObj.getMinutes(), dateObj.getSeconds()); - }, - - /** - * Returns the feed type. - */ - __feedType: null, - _getFeedType: function FW__getFeedType() { - if (this.__feedType != null) - return this.__feedType; - - try { - // grab the feed because it's got the feed.type in it. - var container = this._getContainer(); - var feed = container.QueryInterface(Ci.nsIFeed); - this.__feedType = feed.type; - return feed.type; - } catch (ex) { } - - return Ci.nsIFeed.TYPE_FEED; - }, - - /** - * Maps a feed type to a maybe-feed mimetype. - */ - _getMimeTypeForFeedType: function FW__getMimeTypeForFeedType() { - switch (this._getFeedType()) { - case Ci.nsIFeed.TYPE_VIDEO: - return TYPE_MAYBE_VIDEO_FEED; - - case Ci.nsIFeed.TYPE_AUDIO: - return TYPE_MAYBE_AUDIO_FEED; - - default: - return TYPE_MAYBE_FEED; - } - }, - - /** - * Writes the feed title into the preview document. - * @param container - * The feed container - */ - _setTitleText: function FW__setTitleText(container) { - if (container.title) { - var title = container.title.plainText(); - this._setContentText(TITLE_ID, container.title); - this._contentSandbox.document = this._document; - this._contentSandbox.title = title; - var codeStr = "document.title = title;" - Cu.evalInSandbox(codeStr, this._contentSandbox); - } - - var feed = container.QueryInterface(Ci.nsIFeed); - if (feed && feed.subtitle) - this._setContentText(SUBTITLE_ID, container.subtitle); - }, - - /** - * Writes the title image into the preview document if one is present. - * @param container - * The feed container - */ - _setTitleImage: function FW__setTitleImage(container) { - try { - var parts = container.image; - - // Set up the title image (supplied by the feed) - var feedTitleImage = this._document.getElementById("feedTitleImage"); - this._safeSetURIAttribute(feedTitleImage, "src", - parts.getPropertyAsAString("url")); - - // Set up the title image link - var feedTitleLink = this._document.getElementById("feedTitleLink"); - - var titleText = this._getFormattedString("linkTitleTextFormat", - [parts.getPropertyAsAString("title")]); - this._contentSandbox.feedTitleLink = feedTitleLink; - this._contentSandbox.titleText = titleText; - this._contentSandbox.feedTitleText = this._document.getElementById("feedTitleText"); - this._contentSandbox.titleImageWidth = parseInt(parts.getPropertyAsAString("width")) + 15; - - // Fix the margin on the main title, so that the image doesn't run over - // the underline - var codeStr = "feedTitleLink.setAttribute('title', titleText); " + - "feedTitleText.style.marginRight = titleImageWidth + 'px';"; - Cu.evalInSandbox(codeStr, this._contentSandbox); - this._contentSandbox.feedTitleLink = null; - this._contentSandbox.titleText = null; - this._contentSandbox.feedTitleText = null; - this._contentSandbox.titleImageWidth = null; - - this._safeSetURIAttribute(feedTitleLink, "href", - parts.getPropertyAsAString("link")); - } - catch (e) { - LOG("Failed to set Title Image (this is benign): " + e); - } - }, - - /** - * Writes all entries contained in the feed. - * @param container - * The container of entries in the feed - */ - _writeFeedContent: function FW__writeFeedContent(container) { - // Build the actual feed content - var feed = container.QueryInterface(Ci.nsIFeed); - if (feed.items.length == 0) - return; - - this._contentSandbox.feedContent = - this._document.getElementById("feedContent"); - - for (var i = 0; i < feed.items.length; ++i) { - var entry = feed.items.queryElementAt(i, Ci.nsIFeedEntry); - entry.QueryInterface(Ci.nsIFeedContainer); - - var entryContainer = this._document.createElementNS(HTML_NS, "div"); - entryContainer.className = "entry"; - - // If the entry has a title, make it a link - if (entry.title) { - var a = this._document.createElementNS(HTML_NS, "a"); - var span = this._document.createElementNS(HTML_NS, "span"); - a.appendChild(span); - if (entry.title.base) - span.setAttributeNS(XML_NS, "base", entry.title.base.spec); - span.appendChild(entry.title.createDocumentFragment(a)); - - // Entries are not required to have links, so entry.link can be null. - if (entry.link) - this._safeSetURIAttribute(a, "href", entry.link.spec); - - var title = this._document.createElementNS(HTML_NS, "h3"); - title.appendChild(a); - - var lastUpdated = this._parseDate(entry.updated); - if (lastUpdated) { - var dateDiv = this._document.createElementNS(HTML_NS, "div"); - dateDiv.className = "lastUpdated"; - dateDiv.textContent = lastUpdated; - title.appendChild(dateDiv); - } - - entryContainer.appendChild(title); - } - - var body = this._document.createElementNS(HTML_NS, "div"); - var summary = entry.summary || entry.content; - var docFragment = null; - if (summary) { - if (summary.base) - body.setAttributeNS(XML_NS, "base", summary.base.spec); - else - LOG("no base?"); - docFragment = summary.createDocumentFragment(body); - if (docFragment) - body.appendChild(docFragment); - - // If the entry doesn't have a title, append a # permalink - // See http://scripting.com/rss.xml for an example - if (!entry.title && entry.link) { - var a = this._document.createElementNS(HTML_NS, "a"); - a.appendChild(this._document.createTextNode("#")); - this._safeSetURIAttribute(a, "href", entry.link.spec); - body.appendChild(this._document.createTextNode(" ")); - body.appendChild(a); - } - - } - body.className = "feedEntryContent"; - entryContainer.appendChild(body); - - if (entry.enclosures && entry.enclosures.length > 0) { - var enclosuresDiv = this._buildEnclosureDiv(entry); - entryContainer.appendChild(enclosuresDiv); - } - - this._contentSandbox.entryContainer = entryContainer; - this._contentSandbox.clearDiv = - this._document.createElementNS(HTML_NS, "div"); - this._contentSandbox.clearDiv.style.clear = "both"; - - var codeStr = "feedContent.appendChild(entryContainer); " + - "feedContent.appendChild(clearDiv);" - Cu.evalInSandbox(codeStr, this._contentSandbox); - } - - this._contentSandbox.feedContent = null; - this._contentSandbox.entryContainer = null; - this._contentSandbox.clearDiv = null; - }, - - /** - * Takes a url to a media item and returns the best name it can come up with. - * Frequently this is the filename portion (e.g. passing in - * http://example.com/foo.mpeg would return "foo.mpeg"), but in more complex - * cases, this will return the entire url (e.g. passing in - * http://example.com/somedirectory/ would return - * http://example.com/somedirectory/). - * @param aURL - * The URL string from which to create a display name - * @returns a string - */ - _getURLDisplayName: function FW__getURLDisplayName(aURL) { - var url = makeURI(aURL); - url.QueryInterface(Ci.nsIURL); - if (url == null || url.fileName.length == 0) - return decodeURIComponent(aURL); - - return decodeURIComponent(url.fileName); - }, - - /** - * Takes a FeedEntry with enclosures, generates the HTML code to represent - * them, and returns that. - * @param entry - * FeedEntry with enclosures - * @returns element - */ - _buildEnclosureDiv: function FW__buildEnclosureDiv(entry) { - var enclosuresDiv = this._document.createElementNS(HTML_NS, "div"); - enclosuresDiv.className = "enclosures"; - - enclosuresDiv.appendChild(this._document.createTextNode(this._getString("mediaLabel"))); - - var roundme = function(n) { - return (Math.round(n * 100) / 100).toLocaleString(); - } - - for (var i_enc = 0; i_enc < entry.enclosures.length; ++i_enc) { - var enc = entry.enclosures.queryElementAt(i_enc, Ci.nsIWritablePropertyBag2); - - if (!(enc.hasKey("url"))) - continue; - - var enclosureDiv = this._document.createElementNS(HTML_NS, "div"); - enclosureDiv.setAttribute("class", "enclosure"); - - var mozicon = "moz-icon://.txt?size=16"; - var type_text = null; - var size_text = null; - - if (enc.hasKey("type")) { - type_text = enc.get("type"); - try { - var handlerInfoWrapper = this._mimeSvc.getFromTypeAndExtension(enc.get("type"), null); - - if (handlerInfoWrapper) - type_text = handlerInfoWrapper.description; - - if (type_text && type_text.length > 0) - mozicon = "moz-icon://goat?size=16&contentType=" + enc.get("type"); - - } catch (ex) { } - - } - - if (enc.hasKey("length") && /^[0-9]+$/.test(enc.get("length"))) { - var enc_size = convertByteUnits(parseInt(enc.get("length"))); - - var size_text = this._getFormattedString("enclosureSizeText", - [enc_size[0], this._getString(enc_size[1])]); - } - - var iconimg = this._document.createElementNS(HTML_NS, "img"); - iconimg.setAttribute("src", mozicon); - iconimg.setAttribute("class", "type-icon"); - enclosureDiv.appendChild(iconimg); - - enclosureDiv.appendChild(this._document.createTextNode( " " )); - - var enc_href = this._document.createElementNS(HTML_NS, "a"); - enc_href.appendChild(this._document.createTextNode(this._getURLDisplayName(enc.get("url")))); - this._safeSetURIAttribute(enc_href, "href", enc.get("url")); - enclosureDiv.appendChild(enc_href); - - if (type_text && size_text) - enclosureDiv.appendChild(this._document.createTextNode( " (" + type_text + ", " + size_text + ")")); - - else if (type_text) - enclosureDiv.appendChild(this._document.createTextNode( " (" + type_text + ")")) - - else if (size_text) - enclosureDiv.appendChild(this._document.createTextNode( " (" + size_text + ")")) - - enclosuresDiv.appendChild(enclosureDiv); - } - - return enclosuresDiv; - }, - - /** - * Gets a valid nsIFeedContainer object from the parsed nsIFeedResult. - * Displays error information if there was one. - * @param result - * The parsed feed result - * @returns A valid nsIFeedContainer object containing the contents of - * the feed. - */ - _getContainer: function FW__getContainer(result) { - var feedService = - Cc["@mozilla.org/browser/feeds/result-service;1"]. - getService(Ci.nsIFeedResultService); - - result = null; - try { - result = - feedService.getFeedResult(this._getOriginalURI(this._window)); - } - catch (e) { - // Ignore. - } - - if (!result) { - LOG("Subscribe Preview: feed not available?!"); - return null; - } - - if (result.bozo) { - LOG("Subscribe Preview: feed result is bozo?!"); - } - - try { - var container = result.doc; - } - catch (e) { - LOG("Subscribe Preview: no result.doc? Why didn't the original reload?"); - return null; - } - return container; - }, - - /** - * Get the human-readable display name of a file. This could be the - * application name. - * @param file - * A nsIFile to look up the name of - * @returns The display name of the application represented by the file. - */ - _getFileDisplayName: function FW__getFileDisplayName(file) { -#ifdef XP_WIN - if (file instanceof Ci.nsILocalFileWin) { - try { - return file.getVersionInfoField("FileDescription"); - } catch (e) {} - } -#endif -#ifdef XP_MACOSX - if (file instanceof Ci.nsILocalFileMac) { - try { - return file.bundleDisplayName; - } catch (e) {} - } -#endif - return file.leafName; - }, - - /** - * Helper method to set the selected application and system default - * reader menuitems details from a file object - * @param aMenuItem - * The menuitem on which the attributes should be set - * @param aFile - * The menuitem's associated file - */ - _initMenuItemWithFile: function(aMenuItem, aFile) { - this._contentSandbox.menuitem = aMenuItem; - this._contentSandbox.label = this._getFileDisplayName(aFile); - // For security reasons, access to moz-icon:file://... URIs is - // no longer allowed (indirect file system access from content). - // We use a dummy application instead to get a generic icon. - this._contentSandbox.image = "moz-icon://dummy.exe?size=16"; - var codeStr = "menuitem.setAttribute('label', label); " + - "menuitem.setAttribute('image', image);" - Cu.evalInSandbox(codeStr, this._contentSandbox); - }, - - /** - * Helper method to get an element in the XBL binding where the handler - * selection UI lives - */ - _getUIElement: function FW__getUIElement(id) { - return this._document.getAnonymousElementByAttribute( - this._document.getElementById("feedSubscribeLine"), "anonid", id); - }, - - /** - * Displays a prompt from which the user may choose a (client) feed reader. - * @param aCallback the callback method, passes in true if a feed reader was - * selected, false otherwise. - */ - _chooseClientApp: function FW__chooseClientApp(aCallback) { - try { - let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker); - let fpCallback = function fpCallback_done(aResult) { - if (aResult == Ci.nsIFilePicker.returnOK) { - this._selectedApp = fp.file; - if (this._selectedApp) { - // XXXben - we need to compare this with the running instance - // executable just don't know how to do that via script - // XXXmano TBD: can probably add this to nsIShellService -#ifdef XP_WIN -#expand if (fp.file.leafName != "__MOZ_APP_NAME__.exe") { -#else -#ifdef XP_MACOSX -#expand if (fp.file.leafName != "__MOZ_MACBUNDLE_NAME__") { -#else -#expand if (fp.file.leafName != "__MOZ_APP_NAME__-bin") { -#endif -#endif - this._initMenuItemWithFile(this._contentSandbox.selectedAppMenuItem, - this._selectedApp); - - // Show and select the selected application menuitem - let codeStr = "selectedAppMenuItem.hidden = false;" + - "selectedAppMenuItem.doCommand();" - Cu.evalInSandbox(codeStr, this._contentSandbox); - if (aCallback) { - aCallback(true); - return; - } - } - } - } - if (aCallback) { - aCallback(false); - } - }.bind(this); - - fp.init(this._window, this._getString("chooseApplicationDialogTitle"), - Ci.nsIFilePicker.modeOpen); - fp.appendFilters(Ci.nsIFilePicker.filterApps); - fp.open(fpCallback); - } catch(ex) { - } - }, - - _setAlwaysUseCheckedState: function FW__setAlwaysUseCheckedState(feedType) { - var checkbox = this._getUIElement("alwaysUse"); - if (checkbox) { - var alwaysUse = false; - try { - var prefs = Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefBranch); - if (prefs.getCharPref(getPrefActionForType(feedType)) != "ask") - alwaysUse = true; - } - catch(ex) { } - this._setCheckboxCheckedState(checkbox, alwaysUse); - } - }, - - _setSubscribeUsingLabel: function FW__setSubscribeUsingLabel() { - var stringLabel = "subscribeFeedUsing"; - switch (this._getFeedType()) { - case Ci.nsIFeed.TYPE_VIDEO: - stringLabel = "subscribeVideoPodcastUsing"; - break; - - case Ci.nsIFeed.TYPE_AUDIO: - stringLabel = "subscribeAudioPodcastUsing"; - break; - } - - this._contentSandbox.subscribeUsing = - this._getUIElement("subscribeUsingDescription"); - this._contentSandbox.label = this._getString(stringLabel); - var codeStr = "subscribeUsing.setAttribute('value', label);" - Cu.evalInSandbox(codeStr, this._contentSandbox); - }, - - _setAlwaysUseLabel: function FW__setAlwaysUseLabel() { - var checkbox = this._getUIElement("alwaysUse"); - if (checkbox) { - if (this._handlersMenuList) { - var handlerName = this._getSelectedItemFromMenulist(this._handlersMenuList) - .getAttribute("label"); - var stringLabel = "alwaysUseForFeeds"; - switch (this._getFeedType()) { - case Ci.nsIFeed.TYPE_VIDEO: - stringLabel = "alwaysUseForVideoPodcasts"; - break; - - case Ci.nsIFeed.TYPE_AUDIO: - stringLabel = "alwaysUseForAudioPodcasts"; - break; - } - - this._contentSandbox.checkbox = checkbox; - this._contentSandbox.label = this._getFormattedString(stringLabel, [handlerName]); - - var codeStr = "checkbox.setAttribute('label', label);"; - Cu.evalInSandbox(codeStr, this._contentSandbox); - } - } - }, - - // nsIDomEventListener - handleEvent: function(event) { - if (event.target.ownerDocument != this._document) { - LOG("FeedWriter.handleEvent: Someone passed the feed writer as a listener to the events of another document!"); - return; - } - - if (event.type == "command") { - switch (event.target.getAttribute("anonid")) { - case "subscribeButton": - this.subscribe(); - break; - case "chooseApplicationMenuItem": - /* Bug 351263: Make sure to not steal focus if the "Choose - * Application" item is being selected with the keyboard. We do this - * by ignoring command events while the dropdown is closed (user - * arrowing through the combobox), but handling them while the - * combobox dropdown is open (user pressed enter when an item was - * selected). If we don't show the filepicker here, it will be shown - * when clicking "Subscribe Now". - */ - var popupbox = this._handlersMenuList.firstChild.boxObject; - if (popupbox.popupState == "hiding") { - this._chooseClientApp(function(aResult) { - if (!aResult) { - // Select the (per-prefs) selected handler if no application - // was selected - this._setSelectedHandler(this._getFeedType()); - } - }.bind(this)); - } - break; - default: - this._setAlwaysUseLabel(); - } - } - }, - - _setSelectedHandler: function FW__setSelectedHandler(feedType) { - var prefs = - Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefBranch); - - var handler = prefs.getCharPref(getPrefReaderForType(feedType), "bookmarks"); - - switch (handler) { - case "web": { - if (this._handlersMenuList) { - var url; - try { - url = prefs.getComplexValue(getPrefWebForType(feedType), Ci.nsISupportsString).data; - } catch (ex) { - LOG("FeedWriter._setSelectedHandler: invalid or no handler in prefs"); - return; - } - var handlers = - this._handlersMenuList.getElementsByAttribute("webhandlerurl", url); - if (handlers.length == 0) { - LOG("FeedWriter._setSelectedHandler: selected web handler isn't in the menulist") - return; - } - - this._safeDoCommand(handlers[0]); - } - break; - } - case "client": { - try { - this._selectedApp = - prefs.getComplexValue(getPrefAppForType(feedType), Ci.nsILocalFile); - } - catch(ex) { - this._selectedApp = null; - } - - if (this._selectedApp) { - this._initMenuItemWithFile(this._contentSandbox.selectedAppMenuItem, - this._selectedApp); - var codeStr = "selectedAppMenuItem.hidden = false; " + - "selectedAppMenuItem.doCommand(); "; - - // Only show the default reader menuitem if the default reader - // isn't the selected application - if (this._defaultSystemReader) { - var shouldHide = - this._defaultSystemReader.path == this._selectedApp.path; - codeStr += "defaultHandlerMenuItem.hidden = " + shouldHide + ";" - } - Cu.evalInSandbox(codeStr, this._contentSandbox); - break; - } - } - case "bookmarks": - default: { - var liveBookmarksMenuItem = this._getUIElement("liveBookmarksMenuItem"); - if (liveBookmarksMenuItem) - this._safeDoCommand(liveBookmarksMenuItem); - } - } - }, - - _initSubscriptionUI: function FW__initSubscriptionUI() { - var handlersMenuPopup = this._getUIElement("handlersMenuPopup"); - if (!handlersMenuPopup) - return; - - var feedType = this._getFeedType(); - var codeStr; - - // change the background - var header = this._document.getElementById("feedHeader"); - this._contentSandbox.header = header; - switch (feedType) { - case Ci.nsIFeed.TYPE_VIDEO: - codeStr = "header.className = 'videoPodcastBackground'; "; - break; - - case Ci.nsIFeed.TYPE_AUDIO: - codeStr = "header.className = 'audioPodcastBackground'; "; - break; - - default: - codeStr = "header.className = 'feedBackground'; "; - } - - var liveBookmarksMenuItem = this._getUIElement("liveBookmarksMenuItem"); - - // Last-selected application - var menuItem = liveBookmarksMenuItem.cloneNode(false); - menuItem.removeAttribute("selected"); - menuItem.setAttribute("anonid", "selectedAppMenuItem"); - menuItem.className = "menuitem-iconic selectedAppMenuItem"; - menuItem.setAttribute("handlerType", "client"); - try { - var prefs = Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefBranch); - this._selectedApp = prefs.getComplexValue(getPrefAppForType(feedType), - Ci.nsILocalFile); - - if (this._selectedApp.exists()) - this._initMenuItemWithFile(menuItem, this._selectedApp); - else { - // Hide the menuitem if the last selected application doesn't exist - menuItem.setAttribute("hidden", true); - } - } - catch(ex) { - // Hide the menuitem until an application is selected - menuItem.setAttribute("hidden", true); - } - this._contentSandbox.handlersMenuPopup = handlersMenuPopup; - this._contentSandbox.selectedAppMenuItem = menuItem; - - codeStr += "handlersMenuPopup.appendChild(selectedAppMenuItem); "; - - // List the default feed reader - try { - this._defaultSystemReader = Cc["@mozilla.org/browser/shell-service;1"]. - getService(Ci.nsIShellService). - defaultFeedReader; - menuItem = liveBookmarksMenuItem.cloneNode(false); - menuItem.removeAttribute("selected"); - menuItem.setAttribute("anonid", "defaultHandlerMenuItem"); - menuItem.className = "menuitem-iconic defaultHandlerMenuItem"; - menuItem.setAttribute("handlerType", "client"); - - this._initMenuItemWithFile(menuItem, this._defaultSystemReader); - - // Hide the default reader item if it points to the same application - // as the last-selected application - if (this._selectedApp && - this._selectedApp.path == this._defaultSystemReader.path) - menuItem.hidden = true; - } - catch(ex) { menuItem = null; /* no default reader */ } - - if (menuItem) { - this._contentSandbox.defaultHandlerMenuItem = menuItem; - codeStr += "handlersMenuPopup.appendChild(defaultHandlerMenuItem); "; - } - - // "Choose Application..." menuitem - menuItem = liveBookmarksMenuItem.cloneNode(false); - menuItem.removeAttribute("selected"); - menuItem.setAttribute("anonid", "chooseApplicationMenuItem"); - menuItem.className = "menuitem-iconic chooseApplicationMenuItem"; - menuItem.setAttribute("label", this._getString("chooseApplicationMenuItem")); - - this._contentSandbox.chooseAppMenuItem = menuItem; - codeStr += "handlersMenuPopup.appendChild(chooseAppMenuItem); "; - - // separator - this._contentSandbox.chooseAppSep = - menuItem = liveBookmarksMenuItem.nextSibling.cloneNode(false); - codeStr += "handlersMenuPopup.appendChild(chooseAppSep); "; - - Cu.evalInSandbox(codeStr, this._contentSandbox); - - // List of web handlers - var wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"]. - getService(Ci.nsIWebContentConverterService); - var handlers = wccr.getContentHandlers(this._getMimeTypeForFeedType(feedType)); - if (handlers.length != 0) { - for (var i = 0; i < handlers.length; ++i) { - if (!handlers[i].uri) { - LOG("Handler with name " + handlers[i].name + " has no URI!? Skipping..."); - continue; - } - menuItem = liveBookmarksMenuItem.cloneNode(false); - menuItem.removeAttribute("selected"); - menuItem.className = "menuitem-iconic"; - menuItem.setAttribute("label", handlers[i].name); - menuItem.setAttribute("handlerType", "web"); - menuItem.setAttribute("webhandlerurl", handlers[i].uri); - this._contentSandbox.menuItem = menuItem; - codeStr = "handlersMenuPopup.appendChild(menuItem);"; - Cu.evalInSandbox(codeStr, this._contentSandbox); - - this._setFaviconForWebReader(handlers[i].uri, menuItem); - } - this._contentSandbox.menuItem = null; - } - - this._setSelectedHandler(feedType); - - // "Subscribe using..." - this._setSubscribeUsingLabel(); - - // "Always use..." checkbox initial state - this._setAlwaysUseCheckedState(feedType); - this._setAlwaysUseLabel(); - - // We update the "Always use.." checkbox label whenever the selected item - // in the list is changed - handlersMenuPopup.addEventListener("command", this, false); - - // Set up the "Subscribe Now" button - this._getUIElement("subscribeButton") - .addEventListener("command", this, false); - - // first-run ui - var showFirstRunUI = prefs.getBoolPref(PREF_SHOW_FIRST_RUN_UI, true); - if (showFirstRunUI) { - var textfeedinfo1, textfeedinfo2; - switch (feedType) { - case Ci.nsIFeed.TYPE_VIDEO: - textfeedinfo1 = "feedSubscriptionVideoPodcast1"; - textfeedinfo2 = "feedSubscriptionVideoPodcast2"; - break; - case Ci.nsIFeed.TYPE_AUDIO: - textfeedinfo1 = "feedSubscriptionAudioPodcast1"; - textfeedinfo2 = "feedSubscriptionAudioPodcast2"; - break; - default: - textfeedinfo1 = "feedSubscriptionFeed1"; - textfeedinfo2 = "feedSubscriptionFeed2"; - } - - this._contentSandbox.feedinfo1 = - this._document.getElementById("feedSubscriptionInfo1"); - this._contentSandbox.feedinfo1Str = this._getString(textfeedinfo1); - this._contentSandbox.feedinfo2 = - this._document.getElementById("feedSubscriptionInfo2"); - this._contentSandbox.feedinfo2Str = this._getString(textfeedinfo2); - this._contentSandbox.header = header; - codeStr = "feedinfo1.textContent = feedinfo1Str; " + - "feedinfo2.textContent = feedinfo2Str; " + - "header.setAttribute('firstrun', 'true');" - Cu.evalInSandbox(codeStr, this._contentSandbox); - prefs.setBoolPref(PREF_SHOW_FIRST_RUN_UI, false); - } - }, - - /** - * Returns the original URI object of the feed and ensures that this - * component is only ever invoked from the preview document. - * @param aWindow - * The window of the document invoking the BrowserFeedWriter - */ - _getOriginalURI: function FW__getOriginalURI(aWindow) { - var chan = aWindow.QueryInterface(Ci.nsIInterfaceRequestor). - getInterface(Ci.nsIWebNavigation). - QueryInterface(Ci.nsIDocShell).currentDocumentChannel; - - var nullPrincipal = Cc["@mozilla.org/nullprincipal;1"]. - createInstance(Ci.nsIPrincipal); - - // this channel is not going to be openend, use a nullPrincipal - // and the most restrctive securityFlag. - let resolvedURI = NetUtil.newChannel({ - uri: "about:feeds", - loadingPrincipal: nullPrincipal, - securityFlags: Ci.nsILoadInfo.SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED, - contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER - }).URI; - - if (resolvedURI.equals(chan.URI)) - return chan.originalURI; - - return null; - }, - - _window: null, - _document: null, - _feedURI: null, - _feedPrincipal: null, - _handlersMenuList: null, - - // BrowserFeedWriter WebIDL methods - init: function FW_init(aWindow) { - var window = aWindow; - this._feedURI = this._getOriginalURI(window); - if (!this._feedURI) - return; - - this._window = window; - this._document = window.document; - this._document.getElementById("feedSubscribeLine").offsetTop; - this._handlersMenuList = this._getUIElement("handlersMenuList"); - - var secman = Cc["@mozilla.org/scriptsecuritymanager;1"]. - getService(Ci.nsIScriptSecurityManager); - this._feedPrincipal = secman.createCodebasePrincipal(this._feedURI, {}); - - LOG("Subscribe Preview: feed uri = " + this._window.location.href); - - // Set up the subscription UI - this._initSubscriptionUI(); - var prefs = Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefBranch); - prefs.addObserver(PREF_SELECTED_ACTION, this, false); - prefs.addObserver(PREF_SELECTED_READER, this, false); - prefs.addObserver(PREF_SELECTED_WEB, this, false); - prefs.addObserver(PREF_SELECTED_APP, this, false); - prefs.addObserver(PREF_VIDEO_SELECTED_ACTION, this, false); - prefs.addObserver(PREF_VIDEO_SELECTED_READER, this, false); - prefs.addObserver(PREF_VIDEO_SELECTED_WEB, this, false); - prefs.addObserver(PREF_VIDEO_SELECTED_APP, this, false); - - prefs.addObserver(PREF_AUDIO_SELECTED_ACTION, this, false); - prefs.addObserver(PREF_AUDIO_SELECTED_READER, this, false); - prefs.addObserver(PREF_AUDIO_SELECTED_WEB, this, false); - prefs.addObserver(PREF_AUDIO_SELECTED_APP, this, false); - }, - - writeContent: function FW_writeContent() { - if (!this._window) - return; - - try { - // Set up the feed content - var container = this._getContainer(); - if (!container) - return; - - this._setTitleText(container); - this._setTitleImage(container); - this._writeFeedContent(container); - } - finally { - this._removeFeedFromCache(); - } - }, - - close: function FW_close() { - this._getUIElement("handlersMenuPopup") - .removeEventListener("command", this, false); - this._getUIElement("subscribeButton") - .removeEventListener("command", this, false); - this._document = null; - this._window = null; - var prefs = Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefBranch); - prefs.removeObserver(PREF_SELECTED_ACTION, this); - prefs.removeObserver(PREF_SELECTED_READER, this); - prefs.removeObserver(PREF_SELECTED_WEB, this); - prefs.removeObserver(PREF_SELECTED_APP, this); - prefs.removeObserver(PREF_VIDEO_SELECTED_ACTION, this); - prefs.removeObserver(PREF_VIDEO_SELECTED_READER, this); - prefs.removeObserver(PREF_VIDEO_SELECTED_WEB, this); - prefs.removeObserver(PREF_VIDEO_SELECTED_APP, this); - - prefs.removeObserver(PREF_AUDIO_SELECTED_ACTION, this); - prefs.removeObserver(PREF_AUDIO_SELECTED_READER, this); - prefs.removeObserver(PREF_AUDIO_SELECTED_WEB, this); - prefs.removeObserver(PREF_AUDIO_SELECTED_APP, this); - - this._removeFeedFromCache(); - this.__faviconService = null; - this.__bundle = null; - this._feedURI = null; - this.__contentSandbox = null; - }, - - _removeFeedFromCache: function FW__removeFeedFromCache() { - if (this._feedURI) { - var feedService = Cc["@mozilla.org/browser/feeds/result-service;1"]. - getService(Ci.nsIFeedResultService); - feedService.removeFeedResult(this._feedURI); - this._feedURI = null; - } - }, - - subscribe: function FW_subscribe() { - var feedType = this._getFeedType(); - - // Subscribe to the feed using the selected handler and save prefs - var prefs = Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefBranch); - var defaultHandler = "reader"; - var useAsDefault = this._getUIElement("alwaysUse").getAttribute("checked"); - - var selectedItem = this._getSelectedItemFromMenulist(this._handlersMenuList); - let subscribeCallback = function() { - if (selectedItem.hasAttribute("webhandlerurl")) { - var webURI = selectedItem.getAttribute("webhandlerurl"); - prefs.setCharPref(getPrefReaderForType(feedType), "web"); - - var supportsString = Cc["@mozilla.org/supports-string;1"]. - createInstance(Ci.nsISupportsString); - supportsString.data = webURI; - prefs.setComplexValue(getPrefWebForType(feedType), Ci.nsISupportsString, - supportsString); - - var wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"]. - getService(Ci.nsIWebContentConverterService); - var handler = wccr.getWebContentHandlerByURI(this._getMimeTypeForFeedType(feedType), webURI); - if (handler) { - if (useAsDefault) { - wccr.setAutoHandler(this._getMimeTypeForFeedType(feedType), handler); - } - - this._window.location.href = handler.getHandlerURI(this._window.location.href); - } - } else { - switch (selectedItem.getAttribute("anonid")) { - case "selectedAppMenuItem": - prefs.setComplexValue(getPrefAppForType(feedType), Ci.nsILocalFile, - this._selectedApp); - prefs.setCharPref(getPrefReaderForType(feedType), "client"); - break; - case "defaultHandlerMenuItem": - prefs.setComplexValue(getPrefAppForType(feedType), Ci.nsILocalFile, - this._defaultSystemReader); - prefs.setCharPref(getPrefReaderForType(feedType), "client"); - break; - case "liveBookmarksMenuItem": - defaultHandler = "bookmarks"; - prefs.setCharPref(getPrefReaderForType(feedType), "bookmarks"); - break; - } - var feedService = Cc["@mozilla.org/browser/feeds/result-service;1"]. - getService(Ci.nsIFeedResultService); - - // Pull the title and subtitle out of the document - var feedTitle = this._document.getElementById(TITLE_ID).textContent; - var feedSubtitle = this._document.getElementById(SUBTITLE_ID).textContent; - feedService.addToClientReader(this._window.location.href, feedTitle, feedSubtitle, feedType); - } - - // If "Always use..." is checked, we should set PREF_*SELECTED_ACTION - // to either "reader" (If a web reader or if an application is selected), - // or to "bookmarks" (if the live bookmarks option is selected). - // Otherwise, we should set it to "ask" - if (useAsDefault) { - prefs.setCharPref(getPrefActionForType(feedType), defaultHandler); - } else { - prefs.setCharPref(getPrefActionForType(feedType), "ask"); - } - }.bind(this); - - // Show the file picker before subscribing if the - // choose application menuitem was chosen using the keyboard - if (selectedItem.getAttribute("anonid") == "chooseApplicationMenuItem") { - this._chooseClientApp(function(aResult) { - if (aResult) { - selectedItem = - this._getSelectedItemFromMenulist(this._handlersMenuList); - subscribeCallback(); - } - }.bind(this)); - } else { - subscribeCallback(); - } - }, - - // nsIObserver - observe: function FW_observe(subject, topic, data) { - if (!this._window) { - // this._window is null unless this.init was called with a trusted - // window object. - return; - } - - var feedType = this._getFeedType(); - - if (topic == "nsPref:changed") { - switch (data) { - case PREF_SELECTED_READER: - case PREF_SELECTED_WEB: - case PREF_SELECTED_APP: - case PREF_VIDEO_SELECTED_READER: - case PREF_VIDEO_SELECTED_WEB: - case PREF_VIDEO_SELECTED_APP: - case PREF_AUDIO_SELECTED_READER: - case PREF_AUDIO_SELECTED_WEB: - case PREF_AUDIO_SELECTED_APP: - this._setSelectedHandler(feedType); - break; - case PREF_SELECTED_ACTION: - case PREF_VIDEO_SELECTED_ACTION: - case PREF_AUDIO_SELECTED_ACTION: - this._setAlwaysUseCheckedState(feedType); - } - } - }, - - /** - * Sets the icon for the given web-reader item in the readers menu. - * The icon is fetched and stored through the favicon service. - * - * @param aReaderUrl - * the reader url. - * @param aMenuItem - * the reader item in the readers menulist. - * - * @note For privacy reasons we cannot set the image attribute directly - * to the icon url. See Bug 358878 for details. - */ - _setFaviconForWebReader: - function FW__setFaviconForWebReader(aReaderUrl, aMenuItem) { - var readerURI = makeURI(aReaderUrl); - if (!/^https?$/.test(readerURI.scheme)) { - // Don't try to get a favicon for non http(s) URIs. - return; - } - var faviconURI = makeURI(readerURI.prePath + "/favicon.ico"); - var self = this; - var usePrivateBrowsing = this._window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShell) - .QueryInterface(Ci.nsILoadContext) - .usePrivateBrowsing; - var nullPrincipal = Cc["@mozilla.org/nullprincipal;1"] - .createInstance(Ci.nsIPrincipal); - this._faviconService.setAndFetchFaviconForPage(readerURI, faviconURI, false, - usePrivateBrowsing ? this._faviconService.FAVICON_LOAD_PRIVATE - : this._faviconService.FAVICON_LOAD_NON_PRIVATE, - function (aURI, aDataLen, aData, aMimeType) { - if (aDataLen > 0) { - var dataURL = "data:" + aMimeType + ";base64," + - btoa(String.fromCharCode.apply(null, aData)); - self._contentSandbox.menuItem = aMenuItem; - self._contentSandbox.dataURL = dataURL; - var codeStr = "menuItem.setAttribute('image', dataURL);"; - Cu.evalInSandbox(codeStr, self._contentSandbox); - self._contentSandbox.menuItem = null; - self._contentSandbox.dataURL = null; - } - }, nullPrincipal); - }, - - classID: FEEDWRITER_CID, - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMEventListener, Ci.nsIObserver, - Ci.nsINavHistoryObserver, - Ci.nsIDOMGlobalPropertyInitializer]) -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([FeedWriter]); diff --git a/components/feeds/WebContentConverter.js b/components/feeds/WebContentConverter.js deleted file mode 100644 index 42e2ede..0000000 --- a/components/feeds/WebContentConverter.js +++ /dev/null @@ -1,927 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); -Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); - -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cr = Components.results; - -function LOG(str) { - dump("*** " + str + "\n"); -} - -const WCCR_CONTRACTID = "@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"; -const WCCR_CLASSID = Components.ID("{792a7e82-06a0-437c-af63-b2d12e808acc}"); - -const WCC_CLASSID = Components.ID("{db7ebf28-cc40-415f-8a51-1b111851df1e}"); -const WCC_CLASSNAME = "Web Service Handler"; - -const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed"; -const TYPE_ANY = "*/*"; -const TYPE_BLACKLIST = [ - "application/x-www-form-urlencoded", - "application/xhtml+xml", - "application/xml", - "application/mathml+xml", - "application/xslt+xml", - "application/x-xpinstall", - "image/gif", - "image/jpg", - "image/jpeg", - "image/png", - "image/x-png", - "image/webp", -#ifdef MOZ_JXR - "image/jxr", - "image/vnd.ms-photo", -#endif - "image/svg+xml", - "image/bmp", - "image/x-ms-bmp", - "image/icon", - "image/x-icon", - "image/vnd.microsoft.icon", - "multipart/x-mixed-replace", - "multipart/form-data", - "text/cache-manifest", - "text/css", - "text/xsl", - "text/html", - "text/ping", - "text/plain", - "text/xml", - "text/javascript", // To prevent malicious intent blocking scripting. - "text/ecmascript"]; - -const PREF_CONTENTHANDLERS_AUTO = "browser.contentHandlers.auto."; -const PREF_CONTENTHANDLERS_BRANCH = "browser.contentHandlers.types."; -const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice"; -const PREF_SELECTED_ACTION = "browser.feeds.handler"; -const PREF_SELECTED_READER = "browser.feeds.handler.default"; -const PREF_HANDLER_EXTERNAL_PREFIX = "network.protocol-handler.external"; -const PREF_ALLOW_DIFFERENT_HOST = "gecko.handlerService.allowRegisterFromDifferentHost"; - -const STRING_BUNDLE_URI = "chrome://browser/locale/feeds/subscribe.properties"; - -const NS_ERROR_MODULE_DOM = 2152923136; -const NS_ERROR_DOM_SYNTAX_ERR = NS_ERROR_MODULE_DOM + 12; - -function WebContentConverter() { -} -WebContentConverter.prototype = { - convert: function WCC_convert() { }, - asyncConvertData: function WCC_asyncConvertData() { }, - onDataAvailable: function WCC_onDataAvailable() { }, - onStopRequest: function WCC_onStopRequest() { }, - - onStartRequest: function WCC_onStartRequest(request, context) { - var wccr = - Cc[WCCR_CONTRACTID]. - getService(Ci.nsIWebContentConverterService); - wccr.loadPreferredHandler(request); - }, - - QueryInterface: function WCC_QueryInterface(iid) { - if (iid.equals(Ci.nsIStreamConverter) || - iid.equals(Ci.nsIStreamListener) || - iid.equals(Ci.nsISupports)) - return this; - throw Cr.NS_ERROR_NO_INTERFACE; - } -}; - -var WebContentConverterFactory = { - createInstance: function WCCF_createInstance(outer, iid) { - if (outer != null) - throw Cr.NS_ERROR_NO_AGGREGATION; - return new WebContentConverter().QueryInterface(iid); - }, - - QueryInterface: function WCC_QueryInterface(iid) { - if (iid.equals(Ci.nsIFactory) || - iid.equals(Ci.nsISupports)) - return this; - throw Cr.NS_ERROR_NO_INTERFACE; - } -}; - -function ServiceInfo(contentType, uri, name) { - this._contentType = contentType; - this._uri = uri; - this._name = name; -} -ServiceInfo.prototype = { - /** - * See nsIHandlerApp - */ - get name() { - return this._name; - }, - - /** - * See nsIHandlerApp - */ - equals: function SI_equals(aHandlerApp) { - if (!aHandlerApp) - throw Cr.NS_ERROR_NULL_POINTER; - - if (aHandlerApp instanceof Ci.nsIWebContentHandlerInfo && - aHandlerApp.contentType == this.contentType && - aHandlerApp.uri == this.uri) - return true; - - return false; - }, - - /** - * See nsIWebContentHandlerInfo - */ - get contentType() { - return this._contentType; - }, - - /** - * See nsIWebContentHandlerInfo - */ - get uri() { - return this._uri; - }, - - /** - * See nsIWebContentHandlerInfo - */ - getHandlerURI: function SI_getHandlerURI(uri) { - return this._uri.replace(/%s/gi, encodeURIComponent(uri)); - }, - - QueryInterface: function SI_QueryInterface(iid) { - if (iid.equals(Ci.nsIWebContentHandlerInfo) || - iid.equals(Ci.nsISupports)) - return this; - throw Cr.NS_ERROR_NO_INTERFACE; - } -}; - -function WebContentConverterRegistrar() { - this._contentTypes = { }; - this._autoHandleContentTypes = { }; -} - -WebContentConverterRegistrar.prototype = { - get stringBundle() { - var sb = Cc["@mozilla.org/intl/stringbundle;1"]. - getService(Ci.nsIStringBundleService). - createBundle(STRING_BUNDLE_URI); - delete WebContentConverterRegistrar.prototype.stringBundle; - return WebContentConverterRegistrar.prototype.stringBundle = sb; - }, - - _getFormattedString: function WCCR__getFormattedString(key, params) { - return this.stringBundle.formatStringFromName(key, params, params.length); - }, - - _getString: function WCCR_getString(key) { - return this.stringBundle.GetStringFromName(key); - }, - - /** - * See nsIWebContentConverterService - */ - getAutoHandler: - function WCCR_getAutoHandler(contentType) { - contentType = this._resolveContentType(contentType); - if (contentType in this._autoHandleContentTypes) - return this._autoHandleContentTypes[contentType]; - return null; - }, - - /** - * See nsIWebContentConverterService - */ - setAutoHandler: - function WCCR_setAutoHandler(contentType, handler) { - if (handler && !this._typeIsRegistered(contentType, handler.uri)) - throw Cr.NS_ERROR_NOT_AVAILABLE; - - contentType = this._resolveContentType(contentType); - this._setAutoHandler(contentType, handler); - - var ps = - Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefService); - var autoBranch = ps.getBranch(PREF_CONTENTHANDLERS_AUTO); - if (handler) - autoBranch.setCharPref(contentType, handler.uri); - else if (autoBranch.prefHasUserValue(contentType)) - autoBranch.clearUserPref(contentType); - - ps.savePrefFile(null); - }, - - /** - * Update the internal data structure (not persistent) - */ - _setAutoHandler: - function WCCR__setAutoHandler(contentType, handler) { - if (handler) - this._autoHandleContentTypes[contentType] = handler; - else if (contentType in this._autoHandleContentTypes) - delete this._autoHandleContentTypes[contentType]; - }, - - /** - * See nsIWebContentConverterService - */ - getWebContentHandlerByURI: - function WCCR_getWebContentHandlerByURI(contentType, uri) { - var handlers = this.getContentHandlers(contentType, { }); - for (var i = 0; i < handlers.length; ++i) { - if (handlers[i].uri == uri) - return handlers[i]; - } - return null; - }, - - /** - * See nsIWebContentConverterService - */ - loadPreferredHandler: - function WCCR_loadPreferredHandler(request) { - var channel = request.QueryInterface(Ci.nsIChannel); - var contentType = this._resolveContentType(channel.contentType); - var handler = this.getAutoHandler(contentType); - if (handler) { - request.cancel(Cr.NS_ERROR_FAILURE); - - var webNavigation = - channel.notificationCallbacks.getInterface(Ci.nsIWebNavigation); - webNavigation.loadURI(handler.getHandlerURI(channel.URI.spec), - Ci.nsIWebNavigation.LOAD_FLAGS_NONE, - null, null, null); - } - }, - - /** - * See nsIWebContentConverterService - */ - removeProtocolHandler: - function WCCR_removeProtocolHandler(aProtocol, aURITemplate) { - var eps = Cc["@mozilla.org/uriloader/external-protocol-service;1"]. - getService(Ci.nsIExternalProtocolService); - var handlerInfo = eps.getProtocolHandlerInfo(aProtocol); - var handlers = handlerInfo.possibleApplicationHandlers; - for (let i = 0; i < handlers.length; i++) { - try { // We only want to test web handlers - let handler = handlers.queryElementAt(i, Ci.nsIWebHandlerApp); - if (handler.uriTemplate == aURITemplate) { - handlers.removeElementAt(i); - var hs = Cc["@mozilla.org/uriloader/handler-service;1"]. - getService(Ci.nsIHandlerService); - hs.store(handlerInfo); - return; - } - } catch (e) { /* it wasn't a web handler */ } - } - }, - - /** - * See nsIWebContentConverterService - */ - removeContentHandler: - function WCCR_removeContentHandler(contentType, uri) { - function notURI(serviceInfo) { - return serviceInfo.uri != uri; - } - - if (contentType in this._contentTypes) { - this._contentTypes[contentType] = - this._contentTypes[contentType].filter(notURI); - } - }, - - /** - * - */ - _mappings: { - "application/rss+xml": TYPE_MAYBE_FEED, - "application/atom+xml": TYPE_MAYBE_FEED, - }, - - /** - * These are types for which there is a separate content converter aside - * from our built in generic one. We should not automatically register - * a factory for creating a converter for these types. - */ - _blockedTypes: { - "application/vnd.mozilla.maybe.feed": true, - }, - - /** - * Determines the "internal" content type based on the _mappings. - * @param contentType - * @returns The resolved contentType value. - */ - _resolveContentType: - function WCCR__resolveContentType(contentType) { - if (contentType in this._mappings) - return this._mappings[contentType]; - return contentType; - }, - - _makeURI: function(aURL, aOriginCharset, aBaseURI) { - var ioService = Components.classes["@mozilla.org/network/io-service;1"] - .getService(Components.interfaces.nsIIOService); - return ioService.newURI(aURL, aOriginCharset, aBaseURI); - }, - - _checkAndGetURI: - function WCCR_checkAndGetURI(aURIString, aContentWindow) - { - try { - let baseURI = aContentWindow.document.baseURIObject; - var uri = this._makeURI(aURIString, null, baseURI); - } catch (ex) { - // not supposed to throw according to spec - return; - } - - // For security reasons we reject non-http(s) urls (see bug 354316), - // we may need to revise this once we support more content types - // XXX this should be a "security exception" according to spec, but that - // isn't defined yet. - if (uri.scheme != "http" && uri.scheme != "https") - throw("Permission denied to add " + uri.spec + " as a content or protocol handler"); - - // We also reject handlers registered from a different host (see bug 402287) - // The pref allows us to test the feature - var pb = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); - if (!pb.getBoolPref(PREF_ALLOW_DIFFERENT_HOST) && - (!["http:", "https:"].includes(aContentWindow.location.protocol) || - aContentWindow.location.hostname != uri.host)) { - throw("Permission denied to add " + uri.spec + " as a content or protocol handler"); - } - - // If the uri doesn't contain '%s', it won't be a good handler - if (uri.spec.indexOf("%s") < 0) - throw NS_ERROR_DOM_SYNTAX_ERR; - - return uri; - }, - - /** - * Determines if a web handler is already registered. - * - * @param aProtocol - * The scheme of the web handler we are checking for. - * @param aURITemplate - * The URI template that the handler uses to handle the protocol. - * @return true if it is already registered, false otherwise. - */ - _protocolHandlerRegistered: - function WCCR_protocolHandlerRegistered(aProtocol, aURITemplate) { - var eps = Cc["@mozilla.org/uriloader/external-protocol-service;1"]. - getService(Ci.nsIExternalProtocolService); - var handlerInfo = eps.getProtocolHandlerInfo(aProtocol); - var handlers = handlerInfo.possibleApplicationHandlers; - for (let i = 0; i < handlers.length; i++) { - try { // We only want to test web handlers - let handler = handlers.queryElementAt(i, Ci.nsIWebHandlerApp); - if (handler.uriTemplate == aURITemplate) - return true; - } catch (e) { /* it wasn't a web handler */ } - } - return false; - }, - - /** - * See nsIWebContentHandlerRegistrar - */ - registerProtocolHandler: - function WCCR_registerProtocolHandler(aProtocol, aURIString, aTitle, aContentWindow) { - LOG("registerProtocolHandler(" + aProtocol + "," + aURIString + "," + aTitle + ")"); - - var uri = this._checkAndGetURI(aURIString, aContentWindow); - - // If the protocol handler is already registered, just return early. - if (this._protocolHandlerRegistered(aProtocol, uri.spec)) { - return; - } - - var browserWindow = this._getBrowserWindowForContentWindow(aContentWindow); - if (PrivateBrowsingUtils.isWindowPrivate(browserWindow)) { - // Inside the private browsing mode, we don't want to alert the user to save - // a protocol handler. We log it to the error console so that web developers - // would have some way to tell what's going wrong. - Cc["@mozilla.org/consoleservice;1"]. - getService(Ci.nsIConsoleService). - logStringMessage("Web page denied access to register a protocol handler inside private browsing mode"); - return; - } - - // First, check to make sure this isn't already handled internally (we don't - // want to let them take over, say "chrome"). - var ios = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - var handler = ios.getProtocolHandler(aProtocol); - if (!(handler instanceof Ci.nsIExternalProtocolHandler)) { - // This is handled internally, so we don't want them to register - // XXX this should be a "security exception" according to spec, but that - // isn't defined yet. - throw("Permission denied to add " + aURIString + "as a protocol handler"); - } - - // check if it is in the black list - var pb = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); - var allowed = pb.getBoolPref(PREF_HANDLER_EXTERNAL_PREFIX + "." + aProtocol, - pb.getBoolPref(PREF_HANDLER_EXTERNAL_PREFIX + "-default")); - if (!allowed) { - // XXX this should be a "security exception" according to spec - throw("Not allowed to register a protocol handler for " + aProtocol); - } - - // Now Ask the user and provide the proper callback - var message = this._getFormattedString("addProtocolHandler", - [aTitle, uri.host, aProtocol]); - - var notificationIcon = uri.prePath + "/favicon.ico"; - var notificationValue = "Protocol Registration: " + aProtocol; - var addButton = { - label: this._getString("addProtocolHandlerAddButton"), - accessKey: this._getString("addHandlerAddButtonAccesskey"), - protocolInfo: { protocol: aProtocol, uri: uri.spec, name: aTitle }, - - callback: - function WCCR_addProtocolHandlerButtonCallback(aNotification, aButtonInfo) { - var protocol = aButtonInfo.protocolInfo.protocol; - var uri = aButtonInfo.protocolInfo.uri; - var name = aButtonInfo.protocolInfo.name; - - var handler = Cc["@mozilla.org/uriloader/web-handler-app;1"]. - createInstance(Ci.nsIWebHandlerApp); - handler.name = name; - handler.uriTemplate = uri; - - var eps = Cc["@mozilla.org/uriloader/external-protocol-service;1"]. - getService(Ci.nsIExternalProtocolService); - var handlerInfo = eps.getProtocolHandlerInfo(protocol); - handlerInfo.possibleApplicationHandlers.appendElement(handler, false); - - // Since the user has agreed to add a new handler, chances are good - // that the next time they see a handler of this type, they're going - // to want to use it. Reset the handlerInfo to ask before the next - // use. - handlerInfo.alwaysAskBeforeHandling = true; - - var hs = Cc["@mozilla.org/uriloader/handler-service;1"]. - getService(Ci.nsIHandlerService); - hs.store(handlerInfo); - } - }; - var browserElement = this._getBrowserForContentWindow(browserWindow, aContentWindow); - var notificationBox = browserWindow.gBrowser.getNotificationBox(browserElement); - notificationBox.appendNotification(message, - notificationValue, - notificationIcon, - notificationBox.PRIORITY_INFO_LOW, - [addButton]); - }, - - /** - * See nsIWebContentHandlerRegistrar - * If a DOM window is provided, then the request came from content, so we - * prompt the user to confirm the registration. - */ - registerContentHandler: - function WCCR_registerContentHandler(aContentType, aURIString, aTitle, aContentWindow) { - LOG("registerContentHandler(" + aContentType + "," + aURIString + "," + aTitle + ")"); - - // Check against the type blacklist. - // XXX this should be a "security exception" according to spec, but that - // isn't defined yet. - var contentType = this._resolveContentType(aContentType); - for (let blacklistType of TYPE_BLACKLIST) { - if (contentType == blacklistType) { - console.error("Unable to register content handler for prohibited MIME type %s.", contentType); - return; - } - } - - if (aContentWindow) { - var uri = this._checkAndGetURI(aURIString, aContentWindow); - - var browserWindow = this._getBrowserWindowForContentWindow(aContentWindow); - var browserElement = this._getBrowserForContentWindow(browserWindow, aContentWindow); - var notificationBox = browserWindow.gBrowser.getNotificationBox(browserElement); - this._appendFeedReaderNotification(uri, aTitle, notificationBox); - } - else - this._registerContentHandler(contentType, aURIString, aTitle); - }, - - /** - * Returns the browser chrome window in which the content window is in - */ - _getBrowserWindowForContentWindow: - function WCCR__getBrowserWindowForContentWindow(aContentWindow) { - return aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShellTreeItem) - .rootTreeItem - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindow) - .wrappedJSObject; - }, - - /** - * Returns the <xul:browser> element associated with the given content - * window. - * - * @param aBrowserWindow - * The browser window in which the content window is in. - * @param aContentWindow - * The content window. It's possible to pass a child content window - * (i.e. the content window of a frame/iframe). - */ - _getBrowserForContentWindow: - function WCCR__getBrowserForContentWindow(aBrowserWindow, aContentWindow) { - // This depends on pseudo APIs of browser.js and tabbrowser.xml - aContentWindow = aContentWindow.top; - var browsers = aBrowserWindow.gBrowser.browsers; - for (var i = 0; i < browsers.length; ++i) { - if (browsers[i].contentWindow == aContentWindow) - return browsers[i]; - } - }, - - /** - * Appends a notifcation for the given feed reader details. - * - * The notification could be either a pseudo-dialog which lets - * the user to add the feed reader: - * [ [icon] Add %feed-reader-name% (%feed-reader-host%) as a Feed Reader? (Add) [x] ] - * - * or a simple message for the case where the feed reader is already registered: - * [ [icon] %feed-reader-name% is already registered as a Feed Reader [x] ] - * - * A new notification isn't appended if the given notificationbox has a - * notification for the same feed reader. - * - * @param aURI - * The url of the feed reader as a nsIURI object - * @param aName - * The feed reader name as it was passed to registerContentHandler - * @param aNotificationBox - * The notification box to which a notification might be appended - * @return true if a notification has been appended, false otherwise. - */ - _appendFeedReaderNotification: - function WCCR__appendFeedReaderNotification(aURI, aName, aNotificationBox) { - var uriSpec = aURI.spec; - var notificationValue = "feed reader notification: " + uriSpec; - var notificationIcon = aURI.prePath + "/favicon.ico"; - - // Don't append a new notification if the notificationbox - // has a notification for the given feed reader already - if (aNotificationBox.getNotificationWithValue(notificationValue)) - return false; - - var buttons, message; - if (this.getWebContentHandlerByURI(TYPE_MAYBE_FEED, uriSpec)) - message = this._getFormattedString("handlerRegistered", [aName]); - else { - message = this._getFormattedString("addHandler", [aName, aURI.host]); - var self = this; - var addButton = { - _outer: self, - label: self._getString("addHandlerAddButton"), - accessKey: self._getString("addHandlerAddButtonAccesskey"), - feedReaderInfo: { uri: uriSpec, name: aName }, - - /* static */ - callback: - function WCCR__addFeedReaderButtonCallback(aNotification, aButtonInfo) { - var uri = aButtonInfo.feedReaderInfo.uri; - var name = aButtonInfo.feedReaderInfo.name; - var outer = aButtonInfo._outer; - - // The reader could have been added from another window mean while - if (!outer.getWebContentHandlerByURI(TYPE_MAYBE_FEED, uri)) - outer._registerContentHandler(TYPE_MAYBE_FEED, uri, name); - - // avoid reference cycles - aButtonInfo._outer = null; - - return false; - } - }; - buttons = [addButton]; - } - - aNotificationBox.appendNotification(message, - notificationValue, - notificationIcon, - aNotificationBox.PRIORITY_INFO_LOW, - buttons); - return true; - }, - - /** - * Save Web Content Handler metadata to persistent preferences. - * @param contentType - * The content Type being handled - * @param uri - * The uri of the web service - * @param title - * The human readable name of the web service - * - * This data is stored under: - * - * browser.contentHandlers.type0 = content/type - * browser.contentHandlers.uri0 = http://www.foo.com/q=%s - * browser.contentHandlers.title0 = Foo 2.0alphr - */ - _saveContentHandlerToPrefs: - function WCCR__saveContentHandlerToPrefs(contentType, uri, title) { - var ps = - Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefService); - var i = 0; - var typeBranch = null; - while (true) { - typeBranch = - ps.getBranch(PREF_CONTENTHANDLERS_BRANCH + i + "."); - try { - typeBranch.getCharPref("type"); - ++i; - } - catch (e) { - // No more handlers - break; - } - } - if (typeBranch) { - typeBranch.setCharPref("type", contentType); - var pls = - Cc["@mozilla.org/pref-localizedstring;1"]. - createInstance(Ci.nsIPrefLocalizedString); - pls.data = uri; - typeBranch.setComplexValue("uri", Ci.nsIPrefLocalizedString, pls); - pls.data = title; - typeBranch.setComplexValue("title", Ci.nsIPrefLocalizedString, pls); - - ps.savePrefFile(null); - } - }, - - /** - * Determines if there is a type with a particular uri registered for the - * specified content type already. - * @param contentType - * The content type that the uri handles - * @param uri - * The uri of the - */ - _typeIsRegistered: function WCCR__typeIsRegistered(contentType, uri) { - if (!(contentType in this._contentTypes)) - return false; - - var services = this._contentTypes[contentType]; - for (var i = 0; i < services.length; ++i) { - // This uri has already been registered - if (services[i].uri == uri) - return true; - } - return false; - }, - - /** - * Gets a stream converter contract id for the specified content type. - * @param contentType - * The source content type for the conversion. - * @returns A contract id to construct a converter to convert between the - * contentType and *\/*. - */ - _getConverterContractID: function WCCR__getConverterContractID(contentType) { - const template = "@mozilla.org/streamconv;1?from=%s&to=*/*"; - return template.replace(/%s/, contentType); - }, - - /** - * Register a web service handler for a content type. - * - * @param contentType - * the content type being handled - * @param uri - * the URI of the web service - * @param title - * the human readable name of the web service - */ - _registerContentHandler: - function WCCR__registerContentHandler(contentType, uri, title) { - this._updateContentTypeHandlerMap(contentType, uri, title); - this._saveContentHandlerToPrefs(contentType, uri, title); - - if (contentType == TYPE_MAYBE_FEED) { - // Make the new handler the last-selected reader in the preview page - // and make sure the preview page is shown the next time a feed is visited - var pb = Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefService).getBranch(null); - pb.setCharPref(PREF_SELECTED_READER, "web"); - - var supportsString = - Cc["@mozilla.org/supports-string;1"]. - createInstance(Ci.nsISupportsString); - supportsString.data = uri; - pb.setComplexValue(PREF_SELECTED_WEB, Ci.nsISupportsString, - supportsString); - pb.setCharPref(PREF_SELECTED_ACTION, "ask"); - this._setAutoHandler(TYPE_MAYBE_FEED, null); - } - }, - - /** - * Update the content type -> handler map. This mapping is not persisted, use - * registerContentHandler or _saveContentHandlerToPrefs for that purpose. - * @param contentType - * The content Type being handled - * @param uri - * The uri of the web service - * @param title - * The human readable name of the web service - */ - _updateContentTypeHandlerMap: - function WCCR__updateContentTypeHandlerMap(contentType, uri, title) { - if (!(contentType in this._contentTypes)) - this._contentTypes[contentType] = []; - - // Avoid adding duplicates - if (this._typeIsRegistered(contentType, uri)) - return; - - this._contentTypes[contentType].push(new ServiceInfo(contentType, uri, title)); - - if (!(contentType in this._blockedTypes)) { - var converterContractID = this._getConverterContractID(contentType); - var cr = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); - cr.registerFactory(WCC_CLASSID, WCC_CLASSNAME, converterContractID, - WebContentConverterFactory); - } - }, - - /** - * See nsIWebContentConverterService - */ - getContentHandlers: - function WCCR_getContentHandlers(contentType, countRef) { - countRef.value = 0; - if (!(contentType in this._contentTypes)) - return []; - - var handlers = this._contentTypes[contentType]; - countRef.value = handlers.length; - return handlers; - }, - - /** - * See nsIWebContentConverterService - */ - resetHandlersForType: - function WCCR_resetHandlersForType(contentType) { - // currently unused within the tree, so only useful for extensions; previous - // impl. was buggy (and even infinite-looped!), so I argue that this is a - // definite improvement - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - }, - - /** - * Registers a handler from the settings on a preferences branch. - * - * @param branch - * an nsIPrefBranch containing "type", "uri", and "title" preferences - * corresponding to the content handler to be registered - */ - _registerContentHandlerWithBranch: function(branch) { - /** - * Since we support up to six predefined readers, we need to handle gaps - * better, since the first branch with user-added values will be .6 - * - * How we deal with that is to check to see if there's no prefs in the - * branch and stop cycling once that's true. This doesn't fix the case - * where a user manually removes a reader, but that's not supported yet! - */ - var vals = branch.getChildList(""); - if (vals.length == 0) - return; - - try { - var type = branch.getCharPref("type"); - var uri = branch.getComplexValue("uri", Ci.nsIPrefLocalizedString).data; - var title = branch.getComplexValue("title", - Ci.nsIPrefLocalizedString).data; - this._updateContentTypeHandlerMap(type, uri, title); - } - catch(ex) { - // do nothing, the next branch might have values - } - }, - - /** - * Load the auto handler, content handler and protocol tables from - * preferences. - */ - _init: function WCCR__init() { - var ps = - Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefService); - - var kids = ps.getBranch(PREF_CONTENTHANDLERS_BRANCH) - .getChildList(""); - - // first get the numbers of the providers by getting all ###.uri prefs - var nums = []; - for (var i = 0; i < kids.length; i++) { - var match = /^(\d+)\.uri$/.exec(kids[i]); - if (!match) - continue; - else - nums.push(match[1]); - } - - // sort them, to get them back in order - nums.sort(function(a, b) {return a - b;}); - - // now register them - for (var i = 0; i < nums.length; i++) { - var branch = ps.getBranch(PREF_CONTENTHANDLERS_BRANCH + nums[i] + "."); - this._registerContentHandlerWithBranch(branch); - } - - // We need to do this _after_ registering all of the available handlers, - // so that getWebContentHandlerByURI can return successfully. - try { - var autoBranch = ps.getBranch(PREF_CONTENTHANDLERS_AUTO); - var childPrefs = autoBranch.getChildList(""); - for (var i = 0; i < childPrefs.length; ++i) { - var type = childPrefs[i]; - var uri = autoBranch.getCharPref(type); - if (uri) { - var handler = this.getWebContentHandlerByURI(type, uri); - this._setAutoHandler(type, handler); - } - } - } - catch (e) { - // No auto branch yet, that's fine - //LOG("WCCR.init: There is no auto branch, benign"); - } - }, - - /** - * See nsIObserver - */ - observe: function WCCR_observe(subject, topic, data) { - var os = - Cc["@mozilla.org/observer-service;1"]. - getService(Ci.nsIObserverService); - switch (topic) { - case "app-startup": - os.addObserver(this, "browser-ui-startup-complete", false); - break; - case "browser-ui-startup-complete": - os.removeObserver(this, "browser-ui-startup-complete"); - this._init(); - break; - } - }, - - /** - * See nsIFactory - */ - createInstance: function WCCR_createInstance(outer, iid) { - if (outer != null) - throw Cr.NS_ERROR_NO_AGGREGATION; - return this.QueryInterface(iid); - }, - - classID: WCCR_CLASSID, - - /** - * See nsISupports - */ - QueryInterface: XPCOMUtils.generateQI( - [Ci.nsIWebContentConverterService, - Ci.nsIWebContentHandlerRegistrar, - Ci.nsIObserver, - Ci.nsIFactory]), - - _xpcom_categories: [{ - category: "app-startup", - service: true - }] -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WebContentConverterRegistrar]); diff --git a/components/feeds/content/subscribe.css b/components/feeds/content/subscribe.css deleted file mode 100644 index bf2524d..0000000 --- a/components/feeds/content/subscribe.css +++ /dev/null @@ -1,7 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#feedSubscribeLine { - -moz-binding: url(subscribe.xml#feedreaderUI); -} diff --git a/components/feeds/content/subscribe.js b/components/feeds/content/subscribe.js deleted file mode 100644 index ab2eac4..0000000 --- a/components/feeds/content/subscribe.js +++ /dev/null @@ -1,23 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var SubscribeHandler = { - /** - * The nsIFeedWriter object that produces the UI - */ - _feedWriter: null, - - init: function SH_init() { - this._feedWriter = new BrowserFeedWriter(); - }, - - writeContent: function SH_writeContent() { - this._feedWriter.writeContent(); - }, - - uninit: function SH_uninit() { - this._feedWriter.close(); - } -}; diff --git a/components/feeds/content/subscribe.xhtml b/components/feeds/content/subscribe.xhtml deleted file mode 100644 index 8ad069f..0000000 --- a/components/feeds/content/subscribe.xhtml +++ /dev/null @@ -1,65 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - - -<!DOCTYPE html [ - <!ENTITY % htmlDTD - PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" - "DTD/xhtml1-strict.dtd"> - %htmlDTD; - <!ENTITY % globalDTD - SYSTEM "chrome://global/locale/global.dtd"> - %globalDTD; - <!ENTITY % feedDTD - SYSTEM "chrome://browser/locale/feeds/subscribe.dtd"> - %feedDTD; -]> - -<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> - -<html id="feedHandler" - xmlns="http://www.w3.org/1999/xhtml"> - <head> - <title>&feedPage.title;</title> - <link rel="stylesheet" - href="chrome://browser/skin/feeds/subscribe.css" - type="text/css" - media="all"/> - <link rel="stylesheet" - href="chrome://browser/content/feeds/subscribe.css" - type="text/css" - media="all"/> - <script type="application/javascript" - src="chrome://browser/content/feeds/subscribe.js"/> - </head> - <body onload="SubscribeHandler.writeContent();" onunload="SubscribeHandler.uninit();"> - <div id="feedHeaderContainer"> - <div id="feedHeader" dir="&locale.dir;"> - <div id="feedIntroText"> - <p id="feedSubscriptionInfo1" /> - <p id="feedSubscriptionInfo2" /> - </div> - <div id="feedSubscribeLine"></div> - </div> - </div> - - <script type="application/javascript"> - SubscribeHandler.init(); - </script> - - <div id="feedBody"> - <div id="feedTitle"> - <a id="feedTitleLink"> - <img id="feedTitleImage"/> - </a> - <div id="feedTitleContainer"> - <h1 id="feedTitleText"/> - <h2 id="feedSubtitleText"/> - </div> - </div> - <div id="feedContent"/> - </div> - </body> -</html> diff --git a/components/feeds/content/subscribe.xml b/components/feeds/content/subscribe.xml deleted file mode 100644 index 949bcfd..0000000 --- a/components/feeds/content/subscribe.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<!DOCTYPE bindings [ - <!ENTITY % feedDTD - SYSTEM "chrome://browser/locale/feeds/subscribe.dtd"> - %feedDTD; -]> -<bindings id="feedBindings" - xmlns="http://www.mozilla.org/xbl" - xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - <binding id="feedreaderUI" bindToUntrustedContent="true"> - <content> - <xul:vbox> - <xul:hbox align="center"> - <xul:description anonid="subscribeUsingDescription" class="subscribeUsingDescription"/> - <xul:menulist anonid="handlersMenuList" class="handlersMenuList" aria-labelledby="subscribeUsingDescription"> - <xul:menupopup anonid="handlersMenuPopup" class="handlersMenuPopup"> - <xul:menuitem anonid="liveBookmarksMenuItem" label="&feedLiveBookmarks;" class="menuitem-iconic liveBookmarksMenuItem" image="chrome://browser/skin/page-livemarks.png" selected="true"/> - <xul:menuseparator/> - </xul:menupopup> - </xul:menulist> - </xul:hbox> - <xul:hbox> - <xul:checkbox anonid="alwaysUse" class="alwaysUse" checked="false"/> - </xul:hbox> - <xul:hbox align="center"> - <xul:spacer flex="1"/> - <xul:button label="&feedSubscribeNow;" anonid="subscribeButton" class="subscribeButton"/> - </xul:hbox> - </xul:vbox> - </content> - <resources> - <stylesheet src="chrome://browser/skin/feeds/subscribe-ui.css"/> - </resources> - </binding> -</bindings> - diff --git a/components/feeds/jar.mn b/components/feeds/jar.mn deleted file mode 100644 index f8896f8..0000000 --- a/components/feeds/jar.mn +++ /dev/null @@ -1,9 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -browser.jar: - content/browser/feeds/subscribe.xhtml (content/subscribe.xhtml) - content/browser/feeds/subscribe.js (content/subscribe.js) - content/browser/feeds/subscribe.xml (content/subscribe.xml) - content/browser/feeds/subscribe.css (content/subscribe.css) diff --git a/components/feeds/moz.build b/components/feeds/moz.build deleted file mode 100644 index 736920a..0000000 --- a/components/feeds/moz.build +++ /dev/null @@ -1,33 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -JAR_MANIFESTS += ['jar.mn'] - -XPIDL_SOURCES += [ - 'nsIFeedResultService.idl', - 'nsIWebContentConverterRegistrar.idl', -] - -XPIDL_MODULE = 'browser-feeds' - -SOURCES += ['nsFeedSniffer.cpp'] - -EXTRA_COMPONENTS += [ - 'BrowserFeeds.manifest', - 'FeedConverter.js', -] - -EXTRA_PP_COMPONENTS += [ - 'FeedWriter.js', - 'WebContentConverter.js', -] - -FINAL_LIBRARY = 'browsercomps' - -for var in ('MOZ_APP_NAME', 'MOZ_MACBUNDLE_NAME'): - DEFINES[var] = CONFIG[var] - -LOCAL_INCLUDES += ['../build'] diff --git a/components/feeds/nsFeedSniffer.cpp b/components/feeds/nsFeedSniffer.cpp deleted file mode 100644 index f314d3d..0000000 --- a/components/feeds/nsFeedSniffer.cpp +++ /dev/null @@ -1,363 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsFeedSniffer.h" - - -#include "nsNetCID.h" -#include "nsXPCOM.h" -#include "nsCOMPtr.h" -#include "nsStringStream.h" - -#include "nsBrowserCompsCID.h" - -#include "nsICategoryManager.h" -#include "nsIServiceManager.h" -#include "nsComponentManagerUtils.h" -#include "nsServiceManagerUtils.h" - -#include "nsIStreamConverterService.h" -#include "nsIStreamConverter.h" - -#include "nsIStreamListener.h" - -#include "nsIHttpChannel.h" -#include "nsIMIMEHeaderParam.h" - -#include "nsMimeTypes.h" -#include "nsIURI.h" -#include <algorithm> - -#define TYPE_ATOM "application/atom+xml" -#define TYPE_RSS "application/rss+xml" -#define TYPE_MAYBE_FEED "application/vnd.mozilla.maybe.feed" - -#define NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#" -#define NS_RSS "http://purl.org/rss/1.0/" - -#define MAX_BYTES 512u - -NS_IMPL_ISUPPORTS(nsFeedSniffer, - nsIContentSniffer, - nsIStreamListener, - nsIRequestObserver) - -nsresult -nsFeedSniffer::ConvertEncodedData(nsIRequest* request, - const uint8_t* data, - uint32_t length) -{ - nsresult rv = NS_OK; - - mDecodedData = ""; - nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request)); - if (!httpChannel) - return NS_ERROR_NO_INTERFACE; - - nsAutoCString contentEncoding; - httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Encoding"), - contentEncoding); - if (!contentEncoding.IsEmpty()) { - nsCOMPtr<nsIStreamConverterService> converterService(do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID)); - if (converterService) { - ToLowerCase(contentEncoding); - - nsCOMPtr<nsIStreamListener> converter; - rv = converterService->AsyncConvertData(contentEncoding.get(), - "uncompressed", this, nullptr, - getter_AddRefs(converter)); - NS_ENSURE_SUCCESS(rv, rv); - - converter->OnStartRequest(request, nullptr); - - nsCOMPtr<nsIStringInputStream> rawStream = - do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID); - if (!rawStream) - return NS_ERROR_FAILURE; - - rv = rawStream->SetData((const char*)data, length); - NS_ENSURE_SUCCESS(rv, rv); - - rv = converter->OnDataAvailable(request, nullptr, rawStream, 0, length); - NS_ENSURE_SUCCESS(rv, rv); - - converter->OnStopRequest(request, nullptr, NS_OK); - } - } - return rv; -} - -template<int N> -static bool -StringBeginsWithLowercaseLiteral(nsAString& aString, - const char (&aSubstring)[N]) -{ - return StringHead(aString, N).LowerCaseEqualsLiteral(aSubstring); -} - -bool -HasAttachmentDisposition(nsIHttpChannel* httpChannel) -{ - if (!httpChannel) - return false; - - uint32_t disp; - nsresult rv = httpChannel->GetContentDisposition(&disp); - - if (NS_SUCCEEDED(rv) && disp == nsIChannel::DISPOSITION_ATTACHMENT) - return true; - - return false; -} - -/** - * @return the first occurrence of a character within a string buffer, - * or nullptr if not found - */ -static const char* -FindChar(char c, const char *begin, const char *end) -{ - for (; begin < end; ++begin) { - if (*begin == c) - return begin; - } - return nullptr; -} - -/** - * - * Determine if a substring is the "documentElement" in the document. - * - * All of our sniffed substrings: <rss, <feed, <rdf:RDF must be the "document" - * element within the XML DOM, i.e. the root container element. Otherwise, - * it's possible that someone embedded one of these tags inside a document of - * another type, e.g. a HTML document, and we don't want to show the preview - * page if the document isn't actually a feed. - * - * @param start - * The beginning of the data being sniffed - * @param end - * The end of the data being sniffed, right before the substring that - * was found. - * @returns true if the found substring is the documentElement, false - * otherwise. - */ -static bool -IsDocumentElement(const char *start, const char* end) -{ - // For every tag in the buffer, check to see if it's a PI, Doctype or - // comment, our desired substring or something invalid. - while ( (start = FindChar('<', start, end)) ) { - ++start; - if (start >= end) - return false; - - // Check to see if the character following the '<' is either '?' or '!' - // (processing instruction or doctype or comment)... these are valid nodes - // to have in the prologue. - if (*start != '?' && *start != '!') - return false; - - // Now advance the iterator until the '>' (We do this because we don't want - // to sniff indicator substrings that are embedded within other nodes, e.g. - // comments: <!-- <rdf:RDF .. > --> - start = FindChar('>', start, end); - if (!start) - return false; - - ++start; - } - return true; -} - -/** - * Determines whether or not a string exists as the root element in an XML data - * string buffer. - * @param dataString - * The data being sniffed - * @param substring - * The substring being tested for existence and root-ness. - * @returns true if the substring exists and is the documentElement, false - * otherwise. - */ -static bool -ContainsTopLevelSubstring(nsACString& dataString, const char *substring) -{ - int32_t offset = dataString.Find(substring); - if (offset == -1) - return false; - - const char *begin = dataString.BeginReading(); - - // Only do the validation when we find the substring. - return IsDocumentElement(begin, begin + offset); -} - -NS_IMETHODIMP -nsFeedSniffer::GetMIMETypeFromContent(nsIRequest* request, - const uint8_t* data, - uint32_t length, - nsACString& sniffedType) -{ - nsCOMPtr<nsIHttpChannel> channel(do_QueryInterface(request)); - if (!channel) - return NS_ERROR_NO_INTERFACE; - - // Check that this is a GET request, since you can't subscribe to a POST... - nsAutoCString method; - channel->GetRequestMethod(method); - if (!method.EqualsLiteral("GET")) { - sniffedType.Truncate(); - return NS_OK; - } - - // We need to find out if this is a load of a view-source document. In this - // case we do not want to override the content type, since the source display - // does not need to be converted from feed format to XUL. More importantly, - // we don't want to change the content type from something - // nsContentDLF::CreateInstance knows about (e.g. application/xml, text/html - // etc) to something that only the application fe knows about (maybe.feed) - // thus deactivating syntax highlighting. - nsCOMPtr<nsIURI> originalURI; - channel->GetOriginalURI(getter_AddRefs(originalURI)); - - nsAutoCString scheme; - originalURI->GetScheme(scheme); - if (scheme.EqualsLiteral("view-source")) { - sniffedType.Truncate(); - return NS_OK; - } - - // Check the Content-Type to see if it is set correctly. If it is set to - // something specific that we think is a reliable indication of a feed, don't - // bother sniffing since we assume the site maintainer knows what they're - // doing. - nsAutoCString contentType; - channel->GetContentType(contentType); - bool noSniff = contentType.EqualsLiteral(TYPE_RSS) || - contentType.EqualsLiteral(TYPE_ATOM); - - // Check to see if this was a feed request from the location bar or from - // the feed: protocol. This is also a reliable indication. - // The value of the header doesn't matter. - if (!noSniff) { - nsAutoCString sniffHeader; - nsresult foundHeader = - channel->GetRequestHeader(NS_LITERAL_CSTRING("X-Moz-Is-Feed"), - sniffHeader); - noSniff = NS_SUCCEEDED(foundHeader); - } - - if (noSniff) { - // check for an attachment after we have a likely feed. - if(HasAttachmentDisposition(channel)) { - sniffedType.Truncate(); - return NS_OK; - } - - // set the feed header as a response header, since we have good metadata - // telling us that the feed is supposed to be RSS or Atom - channel->SetResponseHeader(NS_LITERAL_CSTRING("X-Moz-Is-Feed"), - NS_LITERAL_CSTRING("1"), false); - sniffedType.AssignLiteral(TYPE_MAYBE_FEED); - return NS_OK; - } - - // Don't sniff arbitrary types. Limit sniffing to situations that - // we think can reasonably arise. - if (!contentType.EqualsLiteral(TEXT_HTML) && - !contentType.EqualsLiteral(APPLICATION_OCTET_STREAM) && - // Same criterion as XMLHttpRequest. Should we be checking for "+xml" - // and check for text/xml and application/xml by hand instead? - contentType.Find("xml") == -1) { - sniffedType.Truncate(); - return NS_OK; - } - - // Now we need to potentially decompress data served with - // Content-Encoding: gzip - nsresult rv = ConvertEncodedData(request, data, length); - if (NS_FAILED(rv)) - return rv; - - // We cap the number of bytes to scan at MAX_BYTES to prevent picking up - // false positives by accidentally reading document content, e.g. a "how to - // make a feed" page. - const char* testData; - if (mDecodedData.IsEmpty()) { - testData = (const char*)data; - length = std::min(length, MAX_BYTES); - } else { - testData = mDecodedData.get(); - length = std::min(mDecodedData.Length(), MAX_BYTES); - } - - // The strategy here is based on that described in: - // http://blogs.msdn.com/rssteam/articles/PublishersGuide.aspx - // for interoperarbility purposes. - - // Thus begins the actual sniffing. - nsDependentCSubstring dataString((const char*)testData, length); - - bool isFeed = false; - - // RSS 0.91/0.92/2.0 - isFeed = ContainsTopLevelSubstring(dataString, "<rss"); - - // Atom 1.0 - if (!isFeed) - isFeed = ContainsTopLevelSubstring(dataString, "<feed"); - - // RSS 1.0 - if (!isFeed) { - isFeed = ContainsTopLevelSubstring(dataString, "<rdf:RDF") && - dataString.Find(NS_RDF) != -1 && - dataString.Find(NS_RSS) != -1; - } - - // If we sniffed a feed, coerce our internal type - if (isFeed && !HasAttachmentDisposition(channel)) - sniffedType.AssignLiteral(TYPE_MAYBE_FEED); - else - sniffedType.Truncate(); - return NS_OK; -} - -NS_IMETHODIMP -nsFeedSniffer::OnStartRequest(nsIRequest* request, nsISupports* context) -{ - return NS_OK; -} - -nsresult -nsFeedSniffer::AppendSegmentToString(nsIInputStream* inputStream, - void* closure, - const char* rawSegment, - uint32_t toOffset, - uint32_t count, - uint32_t* writeCount) -{ - nsCString* decodedData = static_cast<nsCString*>(closure); - decodedData->Append(rawSegment, count); - *writeCount = count; - return NS_OK; -} - -NS_IMETHODIMP -nsFeedSniffer::OnDataAvailable(nsIRequest* request, nsISupports* context, - nsIInputStream* stream, uint64_t offset, - uint32_t count) -{ - uint32_t read; - return stream->ReadSegments(AppendSegmentToString, &mDecodedData, count, - &read); -} - -NS_IMETHODIMP -nsFeedSniffer::OnStopRequest(nsIRequest* request, nsISupports* context, - nsresult status) -{ - return NS_OK; -} diff --git a/components/feeds/nsFeedSniffer.h b/components/feeds/nsFeedSniffer.h deleted file mode 100644 index a0eb986..0000000 --- a/components/feeds/nsFeedSniffer.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - - -#include "nsIContentSniffer.h" -#include "nsIStreamListener.h" -#include "nsStringAPI.h" -#include "mozilla/Attributes.h" - -class nsFeedSniffer final : public nsIContentSniffer, - nsIStreamListener -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSICONTENTSNIFFER - NS_DECL_NSIREQUESTOBSERVER - NS_DECL_NSISTREAMLISTENER - - static nsresult AppendSegmentToString(nsIInputStream* inputStream, - void* closure, - const char* rawSegment, - uint32_t toOffset, - uint32_t count, - uint32_t* writeCount); - -protected: - ~nsFeedSniffer() {} - - nsresult ConvertEncodedData(nsIRequest* request, const uint8_t* data, - uint32_t length); - -private: - nsCString mDecodedData; -}; - diff --git a/components/feeds/nsIFeedResultService.idl b/components/feeds/nsIFeedResultService.idl deleted file mode 100644 index cb0f332..0000000 --- a/components/feeds/nsIFeedResultService.idl +++ /dev/null @@ -1,66 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" -interface nsIURI; -interface nsIRequest; -interface nsIFeedResult; - -/** - * nsIFeedResultService provides a globally-accessible object for retrieving - * the results of feed processing. - */ -[scriptable, uuid(950a829e-c20e-4dc3-b447-f8b753ae54da)] -interface nsIFeedResultService : nsISupports -{ - /** - * When set to true, forces the preview page to be displayed, regardless - * of the user's preferences. - */ - attribute boolean forcePreviewPage; - - /** - * Adds a URI to the user's specified external feed handler, or live - * bookmarks. - * @param uri - * The uri of the feed to add. - * @param title - * The title of the feed to add. - * @param subtitle - * The subtitle of the feed to add. - * @param feedType - * The nsIFeed type of the feed. See nsIFeed.idl - */ - void addToClientReader(in AUTF8String uri, - in AString title, - in AString subtitle, - in unsigned long feedType); - - /** - * Registers a Feed Result object with a globally accessible service - * so that it can be accessed by a singleton method outside the usual - * flow of control in document loading. - * - * @param feedResult - * An object implementing nsIFeedResult representing the feed. - */ - void addFeedResult(in nsIFeedResult feedResult); - - /** - * Gets a Feed Handler object registered using addFeedResult. - * - * @param uri - * The URI of the feed a handler is being requested for - */ - nsIFeedResult getFeedResult(in nsIURI uri); - - /** - * Unregisters a Feed Handler object registered using addFeedResult. - * @param uri - * The feed URI the handler was registered under. This must be - * the same *instance* the feed was registered under. - */ - void removeFeedResult(in nsIURI uri); -}; diff --git a/components/feeds/nsIWebContentConverterRegistrar.idl b/components/feeds/nsIWebContentConverterRegistrar.idl deleted file mode 100644 index 08ce2f4..0000000 --- a/components/feeds/nsIWebContentConverterRegistrar.idl +++ /dev/null @@ -1,117 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsIMIMEInfo.idl" -#include "nsIWebContentHandlerRegistrar.idl" - -interface nsIRequest; - -[scriptable, uuid(eb361098-5158-4b21-8f98-50b445f1f0b2)] -interface nsIWebContentHandlerInfo : nsIHandlerApp -{ - /** - * The content type handled by the handler - */ - readonly attribute AString contentType; - - /** - * The uri of the handler, with an embedded %s where the URI of the loaded - * document will be encoded. - */ - readonly attribute AString uri; - - /** - * Gets the service URL Spec, with the loading document URI encoded in it. - * @param uri - * The URI of the document being loaded - * @returns The URI of the service with the loading document URI encoded in - * it. - */ - AString getHandlerURI(in AString uri); -}; - -[scriptable, uuid(de7cc06e-e778-45cb-b7db-7a114e1e75b1)] -interface nsIWebContentConverterService : nsIWebContentHandlerRegistrar -{ - /** - * Specifies the handler to be used to automatically handle all links of a - * certain content type from now on. - * @param contentType - * The content type to automatically load with the specified handler - * @param handler - * A web service handler. If this is null, no automatic action is - * performed and the user must choose. - * @throws NS_ERROR_NOT_AVAILABLE if the service refered to by |handler| is - * not already registered. - */ - void setAutoHandler(in AString contentType, in nsIWebContentHandlerInfo handler); - - /** - * Gets the auto handler specified for a particular content type - * @param contentType - * The content type to look up an auto handler for. - * @returns The web service handler that will automatically handle all - * documents of the specified type. null if there is no automatic - * handler. (Handlers may be registered, just none of them specified - * as "automatic"). - */ - nsIWebContentHandlerInfo getAutoHandler(in AString contentType); - - /** - * Gets a web handler for the specified service URI - * @param contentType - * The content type of the service being located - * @param uri - * The service URI of the handler to locate. - * @returns A web service handler that uses the specified uri. - */ - nsIWebContentHandlerInfo getWebContentHandlerByURI(in AString contentType, - in AString uri); - - /** - * Loads the preferred handler when content of a registered type is about - * to be loaded. - * @param request - * The nsIRequest for the load of the content - */ - void loadPreferredHandler(in nsIRequest request); - - /** - * Removes a registered protocol handler - * @param protocol - * The protocol scheme to remove a service handler for - * @param uri - * The uri of the service handler to remove - */ - void removeProtocolHandler(in AString protocol, in AString uri); - - /** - * Removes a registered content handler - * @param contentType - * The content type to remove a service handler for - * @param uri - * The uri of the service handler to remove - */ - void removeContentHandler(in AString contentType, in AString uri); - - /** - * Gets the list of content handlers for a particular type. - * @param contentType - * The content type to get handlers for - * @returns An array of nsIWebContentHandlerInfo objects - */ - void getContentHandlers(in AString contentType, - [optional] out unsigned long count, - [retval,array,size_is(count)] out nsIWebContentHandlerInfo handlers); - - /** - * Resets the list of available content handlers to the default set from - * the distribution. - * @param contentType - * The content type to reset handlers for - */ - void resetHandlersForType(in AString contentType); -}; - diff --git a/components/fuel/fuelApplication.js b/components/fuel/fuelApplication.js deleted file mode 100644 index bc3a091..0000000 --- a/components/fuel/fuelApplication.js +++ /dev/null @@ -1,822 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -const Ci = Components.interfaces; -const Cc = Components.classes; - -Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Deprecated", - "resource://gre/modules/Deprecated.jsm"); - -const APPLICATION_CID = Components.ID("fe74cf80-aa2d-11db-abbd-0800200c9a66"); -const APPLICATION_CONTRACTID = "@mozilla.org/fuel/application;1"; - -//================================================= -// Singleton that holds services and utilities -var Utilities = { - get bookmarks() { - let bookmarks = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. - getService(Ci.nsINavBookmarksService); - this.__defineGetter__("bookmarks", function() bookmarks); - return this.bookmarks; - }, - - get bookmarksObserver() { - let bookmarksObserver = new BookmarksObserver(); - this.__defineGetter__("bookmarksObserver", function() bookmarksObserver); - return this.bookmarksObserver; - }, - - get annotations() { - let annotations = Cc["@mozilla.org/browser/annotation-service;1"]. - getService(Ci.nsIAnnotationService); - this.__defineGetter__("annotations", function() annotations); - return this.annotations; - }, - - get history() { - let history = Cc["@mozilla.org/browser/nav-history-service;1"]. - getService(Ci.nsINavHistoryService); - this.__defineGetter__("history", function() history); - return this.history; - }, - - get windowMediator() { - let windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"]. - getService(Ci.nsIWindowMediator); - this.__defineGetter__("windowMediator", function() windowMediator); - return this.windowMediator; - }, - - makeURI: function fuelutil_makeURI(aSpec) { - if (!aSpec) - return null; - var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); - return ios.newURI(aSpec, null, null); - }, - - free: function fuelutil_free() { - delete this.bookmarks; - delete this.bookmarksObserver; - delete this.annotations; - delete this.history; - delete this.windowMediator; - } -}; - - -//================================================= -// Window implementation - -var fuelWindowMap = new WeakMap(); -function getWindow(aWindow) { - let fuelWindow = fuelWindowMap.get(aWindow); - if (!fuelWindow) { - fuelWindow = new Window(aWindow); - fuelWindowMap.set(aWindow, fuelWindow); - } - return fuelWindow; -} - -// Don't call new Window() directly; use getWindow instead. -function Window(aWindow) { - this._window = aWindow; - this._events = new Events(); - - this._watch("TabOpen"); - this._watch("TabMove"); - this._watch("TabClose"); - this._watch("TabSelect"); -} - -Window.prototype = { - get events() { - return this._events; - }, - - get _tabbrowser() { - return this._window.getBrowser(); - }, - - /* - * Helper used to setup event handlers on the XBL element. Note that the events - * are actually dispatched to tabs, so we capture them. - */ - _watch: function win_watch(aType) { - this._tabbrowser.tabContainer.addEventListener(aType, this, - /* useCapture = */ true); - }, - - handleEvent: function win_handleEvent(aEvent) { - this._events.dispatch(aEvent.type, getBrowserTab(this, aEvent.originalTarget.linkedBrowser)); - }, - - get tabs() { - var tabs = []; - var browsers = this._tabbrowser.browsers; - for (var i=0; i<browsers.length; i++) - tabs.push(getBrowserTab(this, browsers[i])); - return tabs; - }, - - get activeTab() { - return getBrowserTab(this, this._tabbrowser.selectedBrowser); - }, - - open: function win_open(aURI) { - return getBrowserTab(this, this._tabbrowser.addTab(aURI.spec).linkedBrowser); - }, - - QueryInterface: XPCOMUtils.generateQI([Ci.fuelIWindow]) -}; - -//================================================= -// BrowserTab implementation - -var fuelBrowserTabMap = new WeakMap(); -function getBrowserTab(aFUELWindow, aBrowser) { - let fuelBrowserTab = fuelBrowserTabMap.get(aBrowser); - if (!fuelBrowserTab) { - fuelBrowserTab = new BrowserTab(aFUELWindow, aBrowser); - fuelBrowserTabMap.set(aBrowser, fuelBrowserTab); - } - else { - // This tab may have moved to another window, so make sure its cached - // window is up-to-date. - fuelBrowserTab._window = aFUELWindow; - } - - return fuelBrowserTab; -} - -// Don't call new BrowserTab() directly; call getBrowserTab instead. -function BrowserTab(aFUELWindow, aBrowser) { - this._window = aFUELWindow; - this._browser = aBrowser; - this._events = new Events(); - - this._watch("load"); -} - -BrowserTab.prototype = { - get _tabbrowser() { - return this._window._tabbrowser; - }, - - get uri() { - return this._browser.currentURI; - }, - - get index() { - var tabs = this._tabbrowser.tabs; - for (var i=0; i<tabs.length; i++) { - if (tabs[i].linkedBrowser == this._browser) - return i; - } - return -1; - }, - - get events() { - return this._events; - }, - - get window() { - return this._window; - }, - - get document() { - return this._browser.contentDocument; - }, - - /* - * Helper used to setup event handlers on the XBL element - */ - _watch: function bt_watch(aType) { - this._browser.addEventListener(aType, this, - /* useCapture = */ true); - }, - - handleEvent: function bt_handleEvent(aEvent) { - if (aEvent.type == "load") { - if (!(aEvent.originalTarget instanceof Ci.nsIDOMDocument)) - return; - - if (aEvent.originalTarget.defaultView instanceof Ci.nsIDOMWindow && - aEvent.originalTarget.defaultView.frameElement) - return; - } - this._events.dispatch(aEvent.type, this); - }, - /* - * Helper used to determine the index offset of the browsertab - */ - _getTab: function bt_gettab() { - var tabs = this._tabbrowser.tabs; - return tabs[this.index] || null; - }, - - load: function bt_load(aURI) { - this._browser.loadURI(aURI.spec, null, null); - }, - - focus: function bt_focus() { - this._tabbrowser.selectedTab = this._getTab(); - this._tabbrowser.focus(); - }, - - close: function bt_close() { - this._tabbrowser.removeTab(this._getTab()); - }, - - moveBefore: function bt_movebefore(aBefore) { - this._tabbrowser.moveTabTo(this._getTab(), aBefore.index); - }, - - moveToEnd: function bt_moveend() { - this._tabbrowser.moveTabTo(this._getTab(), this._tabbrowser.browsers.length); - }, - - QueryInterface: XPCOMUtils.generateQI([Ci.fuelIBrowserTab]) -}; - - -//================================================= -// Annotations implementation -function Annotations(aId) { - this._id = aId; -} - -Annotations.prototype = { - get names() { - return Utilities.annotations.getItemAnnotationNames(this._id); - }, - - has: function ann_has(aName) { - return Utilities.annotations.itemHasAnnotation(this._id, aName); - }, - - get: function ann_get(aName) { - if (this.has(aName)) - return Utilities.annotations.getItemAnnotation(this._id, aName); - return null; - }, - - set: function ann_set(aName, aValue, aExpiration) { - Utilities.annotations.setItemAnnotation(this._id, aName, aValue, 0, aExpiration); - }, - - remove: function ann_remove(aName) { - if (aName) - Utilities.annotations.removeItemAnnotation(this._id, aName); - }, - - QueryInterface: XPCOMUtils.generateQI([Ci.fuelIAnnotations]) -}; - - -//================================================= -// BookmarksObserver implementation (internal class) -// -// BookmarksObserver is a global singleton which watches the browser's -// bookmarks and sends you events when things change. -// -// You can register three different kinds of event listeners on -// BookmarksObserver, using addListener, addFolderListener, and -// addRootlistener. -// -// - addListener(aId, aEvent, aListener) lets you listen to a specific -// bookmark. You can listen to the "change", "move", and "remove" events. -// -// - addFolderListener(aId, aEvent, aListener) lets you listen to a specific -// bookmark folder. You can listen to "addchild" and "removechild". -// -// - addRootListener(aEvent, aListener) lets you listen to the root bookmark -// node. This lets you hear "add", "remove", and "change" events on all -// bookmarks. -// - -function BookmarksObserver() { - this._eventsDict = {}; - this._folderEventsDict = {}; - this._rootEvents = new Events(); - Utilities.bookmarks.addObserver(this, /* ownsWeak = */ true); -} - -BookmarksObserver.prototype = { - onBeginUpdateBatch: function () {}, - onEndUpdateBatch: function () {}, - onItemVisited: function () {}, - - onItemAdded: function bo_onItemAdded(aId, aFolder, aIndex, aItemType, aURI) { - this._rootEvents.dispatch("add", aId); - this._dispatchToEvents("addchild", aId, this._folderEventsDict[aFolder]); - }, - - onItemRemoved: function bo_onItemRemoved(aId, aFolder, aIndex) { - this._rootEvents.dispatch("remove", aId); - this._dispatchToEvents("remove", aId, this._eventsDict[aId]); - this._dispatchToEvents("removechild", aId, this._folderEventsDict[aFolder]); - }, - - onItemChanged: function bo_onItemChanged(aId, aProperty, aIsAnnotationProperty, aValue) { - this._rootEvents.dispatch("change", aProperty); - this._dispatchToEvents("change", aProperty, this._eventsDict[aId]); - }, - - onItemMoved: function bo_onItemMoved(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) { - this._dispatchToEvents("move", aId, this._eventsDict[aId]); - }, - - _dispatchToEvents: function bo_dispatchToEvents(aEvent, aData, aEvents) { - if (aEvents) { - aEvents.dispatch(aEvent, aData); - } - }, - - _addListenerToDict: function bo_addListenerToDict(aId, aEvent, aListener, aDict) { - var events = aDict[aId]; - if (!events) { - events = new Events(); - aDict[aId] = events; - } - events.addListener(aEvent, aListener); - }, - - _removeListenerFromDict: function bo_removeListenerFromDict(aId, aEvent, aListener, aDict) { - var events = aDict[aId]; - if (!events) { - return; - } - events.removeListener(aEvent, aListener); - if (events._listeners.length == 0) { - delete aDict[aId]; - } - }, - - addListener: function bo_addListener(aId, aEvent, aListener) { - this._addListenerToDict(aId, aEvent, aListener, this._eventsDict); - }, - - removeListener: function bo_removeListener(aId, aEvent, aListener) { - this._removeListenerFromDict(aId, aEvent, aListener, this._eventsDict); - }, - - addFolderListener: function addFolderListener(aId, aEvent, aListener) { - this._addListenerToDict(aId, aEvent, aListener, this._folderEventsDict); - }, - - removeFolderListener: function removeFolderListener(aId, aEvent, aListener) { - this._removeListenerFromDict(aId, aEvent, aListener, this._folderEventsDict); - }, - - addRootListener: function addRootListener(aEvent, aListener) { - this._rootEvents.addListener(aEvent, aListener); - }, - - removeRootListener: function removeRootListener(aEvent, aListener) { - this._rootEvents.removeListener(aEvent, aListener); - }, - - QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarksObserver, - Ci.nsISupportsWeakReference]) -}; - -//================================================= -// Bookmark implementation -// -// Bookmark event listeners are stored in BookmarksObserver, not in the -// Bookmark objects themselves. Thus, you don't have to hold on to a Bookmark -// object in order for your event listener to stay valid, and Bookmark objects -// not kept alive by the extension can be GC'ed. -// -// A consequence of this is that if you have two different Bookmark objects x -// and y for the same bookmark (i.e., x != y but x.id == y.id), and you do -// -// x.addListener("foo", fun); -// y.removeListener("foo", fun); -// -// the second line will in fact remove the listener added in the first line. -// - -function Bookmark(aId, aParent, aType) { - this._id = aId; - this._parent = aParent; - this._type = aType || "bookmark"; - this._annotations = new Annotations(this._id); - - // Our _events object forwards to bookmarksObserver. - var self = this; - this._events = { - addListener: function bookmarkevents_al(aEvent, aListener) { - Utilities.bookmarksObserver.addListener(self._id, aEvent, aListener); - }, - removeListener: function bookmarkevents_rl(aEvent, aListener) { - Utilities.bookmarksObserver.removeListener(self._id, aEvent, aListener); - }, - QueryInterface: XPCOMUtils.generateQI([Ci.extIEvents]) - }; - - // For our onItemMoved listener, which updates this._parent. - Utilities.bookmarks.addObserver(this, /* ownsWeak = */ true); -} - -Bookmark.prototype = { - get id() { - return this._id; - }, - - get title() { - return Utilities.bookmarks.getItemTitle(this._id); - }, - - set title(aTitle) { - Utilities.bookmarks.setItemTitle(this._id, aTitle); - }, - - get uri() { - return Utilities.bookmarks.getBookmarkURI(this._id); - }, - - set uri(aURI) { - return Utilities.bookmarks.changeBookmarkURI(this._id, aURI); - }, - - get description() { - return this._annotations.get("bookmarkProperties/description"); - }, - - set description(aDesc) { - this._annotations.set("bookmarkProperties/description", aDesc, Ci.nsIAnnotationService.EXPIRE_NEVER); - }, - - get keyword() { - return Utilities.bookmarks.getKeywordForBookmark(this._id); - }, - - set keyword(aKeyword) { - Utilities.bookmarks.setKeywordForBookmark(this._id, aKeyword); - }, - - get type() { - return this._type; - }, - - get parent() { - return this._parent; - }, - - set parent(aFolder) { - Utilities.bookmarks.moveItem(this._id, aFolder.id, Utilities.bookmarks.DEFAULT_INDEX); - // this._parent is updated in onItemMoved - }, - - get annotations() { - return this._annotations; - }, - - get events() { - return this._events; - }, - - remove : function bm_remove() { - Utilities.bookmarks.removeItem(this._id); - }, - - onBeginUpdateBatch: function () {}, - onEndUpdateBatch: function () {}, - onItemAdded: function () {}, - onItemVisited: function () {}, - onItemRemoved: function () {}, - onItemChanged: function () {}, - - onItemMoved: function(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) { - if (aId == this._id) { - this._parent = new BookmarkFolder(aNewParent, Utilities.bookmarks.getFolderIdForItem(aNewParent)); - } - }, - - QueryInterface: XPCOMUtils.generateQI([Ci.fuelIBookmark, - Ci.nsINavBookmarksObserver, - Ci.nsISupportsWeakReference]) -}; - - -//================================================= -// BookmarkFolder implementation -// -// As with Bookmark, events on BookmarkFolder are handled by the -// BookmarksObserver singleton. -// - -function BookmarkFolder(aId, aParent) { - this._id = aId; - this._parent = aParent; - this._annotations = new Annotations(this._id); - - // Our event listeners are handled by the BookmarksObserver singleton. This - // is a bit complicated because there are three different kinds of events we - // might want to listen to here: - // - // - If this._parent is null, we're the root bookmark folder, and all our - // listeners should be root listeners. - // - // - Otherwise, events ending with "child" (addchild, removechild) are - // handled by a folder listener. - // - // - Other events are handled by a vanilla bookmark listener. - - var self = this; - this._events = { - addListener: function bmfevents_al(aEvent, aListener) { - if (self._parent) { - if (/child$/.test(aEvent)) { - Utilities.bookmarksObserver.addFolderListener(self._id, aEvent, aListener); - } - else { - Utilities.bookmarksObserver.addListener(self._id, aEvent, aListener); - } - } - else { - Utilities.bookmarksObserver.addRootListener(aEvent, aListener); - } - }, - removeListener: function bmfevents_rl(aEvent, aListener) { - if (self._parent) { - if (/child$/.test(aEvent)) { - Utilities.bookmarksObserver.removeFolderListener(self._id, aEvent, aListener); - } - else { - Utilities.bookmarksObserver.removeListener(self._id, aEvent, aListener); - } - } - else { - Utilities.bookmarksObserver.removeRootListener(aEvent, aListener); - } - }, - QueryInterface: XPCOMUtils.generateQI([Ci.extIEvents]) - }; - - // For our onItemMoved listener, which updates this._parent. - Utilities.bookmarks.addObserver(this, /* ownsWeak = */ true); -} - -BookmarkFolder.prototype = { - get id() { - return this._id; - }, - - get title() { - return Utilities.bookmarks.getItemTitle(this._id); - }, - - set title(aTitle) { - Utilities.bookmarks.setItemTitle(this._id, aTitle); - }, - - get description() { - return this._annotations.get("bookmarkProperties/description"); - }, - - set description(aDesc) { - this._annotations.set("bookmarkProperties/description", aDesc, Ci.nsIAnnotationService.EXPIRE_NEVER); - }, - - get type() { - return "folder"; - }, - - get parent() { - return this._parent; - }, - - set parent(aFolder) { - Utilities.bookmarks.moveItem(this._id, aFolder.id, Utilities.bookmarks.DEFAULT_INDEX); - // this._parent is updated in onItemMoved - }, - - get annotations() { - return this._annotations; - }, - - get events() { - return this._events; - }, - - get children() { - var items = []; - - var options = Utilities.history.getNewQueryOptions(); - var query = Utilities.history.getNewQuery(); - query.setFolders([this._id], 1); - var result = Utilities.history.executeQuery(query, options); - var rootNode = result.root; - rootNode.containerOpen = true; - var cc = rootNode.childCount; - for (var i=0; i<cc; ++i) { - var node = rootNode.getChild(i); - if (node.type == node.RESULT_TYPE_FOLDER) { - var folder = new BookmarkFolder(node.itemId, this._id); - items.push(folder); - } - else if (node.type == node.RESULT_TYPE_SEPARATOR) { - var separator = new Bookmark(node.itemId, this._id, "separator"); - items.push(separator); - } - else { - var bookmark = new Bookmark(node.itemId, this._id, "bookmark"); - items.push(bookmark); - } - } - rootNode.containerOpen = false; - - return items; - }, - - addBookmark: function bmf_addbm(aTitle, aUri) { - var newBookmarkID = Utilities.bookmarks.insertBookmark(this._id, aUri, Utilities.bookmarks.DEFAULT_INDEX, aTitle); - var newBookmark = new Bookmark(newBookmarkID, this, "bookmark"); - return newBookmark; - }, - - addSeparator: function bmf_addsep() { - var newBookmarkID = Utilities.bookmarks.insertSeparator(this._id, Utilities.bookmarks.DEFAULT_INDEX); - var newBookmark = new Bookmark(newBookmarkID, this, "separator"); - return newBookmark; - }, - - addFolder: function bmf_addfolder(aTitle) { - var newFolderID = Utilities.bookmarks.createFolder(this._id, aTitle, Utilities.bookmarks.DEFAULT_INDEX); - var newFolder = new BookmarkFolder(newFolderID, this); - return newFolder; - }, - - remove: function bmf_remove() { - Utilities.bookmarks.removeItem(this._id); - }, - - // observer - onBeginUpdateBatch: function () {}, - onEndUpdateBatch : function () {}, - onItemAdded : function () {}, - onItemRemoved : function () {}, - onItemChanged : function () {}, - - onItemMoved: function bf_onItemMoved(aId, aOldParent, aOldIndex, aNewParent, aNewIndex) { - if (this._id == aId) { - this._parent = new BookmarkFolder(aNewParent, Utilities.bookmarks.getFolderIdForItem(aNewParent)); - } - }, - - QueryInterface: XPCOMUtils.generateQI([Ci.fuelIBookmarkFolder, - Ci.nsINavBookmarksObserver, - Ci.nsISupportsWeakReference]) -}; - -//================================================= -// BookmarkRoots implementation -function BookmarkRoots() { -} - -BookmarkRoots.prototype = { - get menu() { - if (!this._menu) - this._menu = new BookmarkFolder(Utilities.bookmarks.bookmarksMenuFolder, null); - - return this._menu; - }, - - get toolbar() { - if (!this._toolbar) - this._toolbar = new BookmarkFolder(Utilities.bookmarks.toolbarFolder, null); - - return this._toolbar; - }, - - get tags() { - if (!this._tags) - this._tags = new BookmarkFolder(Utilities.bookmarks.tagsFolder, null); - - return this._tags; - }, - - get unfiled() { - if (!this._unfiled) - this._unfiled = new BookmarkFolder(Utilities.bookmarks.unfiledBookmarksFolder, null); - - return this._unfiled; - }, - - QueryInterface: XPCOMUtils.generateQI([Ci.fuelIBookmarkRoots]) -}; - - -//================================================= -// Factory - Treat Application as a singleton -// XXX This is required, because we're registered for the 'JavaScript global -// privileged property' category, whose handler always calls createInstance. -// See bug 386535. -var gSingleton = null; -var ApplicationFactory = { - createInstance: function af_ci(aOuter, aIID) { - if (aOuter != null) - throw Components.results.NS_ERROR_NO_AGGREGATION; - - if (gSingleton == null) { - gSingleton = new Application(); - } - - return gSingleton.QueryInterface(aIID); - } -}; - - -#include ../../../../toolkit/components/exthelper/extApplication.js - -//================================================= -// Application constructor -function Application() { - Deprecated.warning("FUEL is deprecated, you should use the standard Toolkit API instead.", - "https://github.com/MoonchildProductions/UXP/issues/1083"); - this.initToolkitHelpers(); -} - -//================================================= -// Application implementation -function ApplicationPrototype() { - // for nsIClassInfo + XPCOMUtils - this.classID = APPLICATION_CID; - - // redefine the default factory for XPCOMUtils - this._xpcom_factory = ApplicationFactory; - - // for nsISupports - this.QueryInterface = XPCOMUtils.generateQI([ - Ci.fuelIApplication, - Ci.extIApplication, - Ci.nsIObserver, - Ci.nsISupportsWeakReference - ]); - - // for nsIClassInfo - this.classInfo = XPCOMUtils.generateCI({ - classID: APPLICATION_CID, - contractID: APPLICATION_CONTRACTID, - interfaces: [ - Ci.fuelIApplication, - Ci.extIApplication, - Ci.nsIObserver - ], - flags: Ci.nsIClassInfo.SINGLETON - }); - - // for nsIObserver - this.observe = function (aSubject, aTopic, aData) { - // Call the extApplication version of this function first - var superPrototype = Object.getPrototypeOf(Object.getPrototypeOf(this)); - superPrototype.observe.call(this, aSubject, aTopic, aData); - if (aTopic == "xpcom-shutdown") { - this._obs.removeObserver(this, "xpcom-shutdown"); - Utilities.free(); - } - }; - - Object.defineProperty(this, "bookmarks", { - get: function bookmarks () { - let bookmarks = new BookmarkRoots(); - Object.defineProperty(this, "bookmarks", { value: bookmarks }); - return this.bookmarks; - }, - enumerable: true, - configurable: true - }); - - Object.defineProperty(this, "windows", { - get: function windows() { - var win = []; - var browserEnum = Utilities.windowMediator.getEnumerator("navigator:browser"); - - while (browserEnum.hasMoreElements()) - win.push(getWindow(browserEnum.getNext())); - - return win; - }, - enumerable: true, - configurable: true - }); - - Object.defineProperty(this, "activeWindow", { - get: () => getWindow(Utilities.windowMediator.getMostRecentWindow("navigator:browser")), - enumerable: true, - configurable: true - }); - -}; - -// set the proto, defined in extApplication.js -ApplicationPrototype.prototype = extApplication.prototype; - -Application.prototype = new ApplicationPrototype(); - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Application]); - diff --git a/components/fuel/fuelApplication.manifest b/components/fuel/fuelApplication.manifest deleted file mode 100644 index 67e6d0f..0000000 --- a/components/fuel/fuelApplication.manifest +++ /dev/null @@ -1,3 +0,0 @@ -component {fe74cf80-aa2d-11db-abbd-0800200c9a66} fuelApplication.js -contract @mozilla.org/fuel/application;1 {fe74cf80-aa2d-11db-abbd-0800200c9a66} -category JavaScript-global-privileged-property Application @mozilla.org/fuel/application;1 diff --git a/components/fuel/fuelIApplication.idl b/components/fuel/fuelIApplication.idl deleted file mode 100644 index 69b51b0..0000000 --- a/components/fuel/fuelIApplication.idl +++ /dev/null @@ -1,347 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" -#include "extIApplication.idl" - -interface nsIVariant; -interface nsIURI; -interface nsIDOMHTMLDocument; - -interface fuelIBookmarkFolder; -interface fuelIBrowserTab; - -/** - * Interface representing a collection of annotations associated - * with a bookmark or bookmark folder. - */ -[scriptable, uuid(335c9292-91a1-4ca0-ad0b-07d5f63ed6cd)] -interface fuelIAnnotations : nsISupports -{ - /** - * Array of the annotation names associated with the owning item - */ - readonly attribute nsIVariant names; - - /** - * Determines if an annotation exists with the given name. - * @param aName - * The name of the annotation - * @returns true if an annotation exists with the given name, - * false otherwise. - */ - boolean has(in AString aName); - - /** - * Gets the value of an annotation with the given name. - * @param aName - * The name of the annotation - * @returns A variant containing the value of the annotation. Supports - * string, boolean and number. - */ - nsIVariant get(in AString aName); - - /** - * Sets the value of an annotation with the given name. - * @param aName - * The name of the annotation - * @param aValue - * The new value of the annotation. Supports string, boolean - * and number - * @param aExpiration - * The expiration policy for the annotation. - * See nsIAnnotationService. - */ - void set(in AString aName, in nsIVariant aValue, in int32_t aExpiration); - - /** - * Removes the named annotation from the owner item. - * @param aName - * The name of annotation. - */ - void remove(in AString aName); -}; - - -/** - * Interface representing a bookmark item. - */ -[scriptable, uuid(808585b6-7568-4b26-8c62-545221bf2b8c)] -interface fuelIBookmark : nsISupports -{ - /** - * The id of the bookmark. - */ - readonly attribute long long id; - - /** - * The title of the bookmark. - */ - attribute AString title; - - /** - * The uri of the bookmark. - */ - attribute nsIURI uri; - - /** - * The description of the bookmark. - */ - attribute AString description; - - /** - * The keyword associated with the bookmark. - */ - attribute AString keyword; - - /** - * The type of the bookmark. - * values: "bookmark", "separator" - */ - readonly attribute AString type; - - /** - * The parent folder of the bookmark. - */ - attribute fuelIBookmarkFolder parent; - - /** - * The annotations object for the bookmark. - */ - readonly attribute fuelIAnnotations annotations; - - /** - * The events object for the bookmark. - * supports: "remove", "change", "visit", "move" - */ - readonly attribute extIEvents events; - - /** - * Removes the item from the parent folder. Used to - * delete a bookmark or separator - */ - void remove(); -}; - - -/** - * Interface representing a bookmark folder. Folders - * can hold bookmarks, separators and other folders. - */ -[scriptable, uuid(9f42fe20-52de-4a55-8632-a459c7716aa0)] -interface fuelIBookmarkFolder : nsISupports -{ - /** - * The id of the folder. - */ - readonly attribute long long id; - - /** - * The title of the folder. - */ - attribute AString title; - - /** - * The description of the folder. - */ - attribute AString description; - - /** - * The type of the folder. - * values: "folder" - */ - readonly attribute AString type; - - /** - * The parent folder of the folder. - */ - attribute fuelIBookmarkFolder parent; - - /** - * The annotations object for the folder. - */ - readonly attribute fuelIAnnotations annotations; - - /** - * The events object for the folder. - * supports: "add", "addchild", "remove", "removechild", "change", "move" - */ - readonly attribute extIEvents events; - - /** - * Array of all bookmarks, separators and folders contained - * in this folder. - */ - readonly attribute nsIVariant children; - - /** - * Adds a new child bookmark to this folder. - * @param aTitle - * The title of bookmark. - * @param aURI - * The uri of bookmark. - */ - fuelIBookmark addBookmark(in AString aTitle, in nsIURI aURI); - - /** - * Adds a new child separator to this folder. - */ - fuelIBookmark addSeparator(); - - /** - * Adds a new child folder to this folder. - * @param aTitle - * The title of folder. - */ - fuelIBookmarkFolder addFolder(in AString aTitle); - - /** - * Removes the folder from the parent folder. - */ - void remove(); -}; - -/** - * Interface representing a container for bookmark roots. Roots - * are the top level parents for the various types of bookmarks in the system. - */ -[scriptable, uuid(c9a80870-eb3c-11dc-95ff-0800200c9a66)] -interface fuelIBookmarkRoots : nsISupports -{ - /** - * The folder for the 'bookmarks menu' root. - */ - readonly attribute fuelIBookmarkFolder menu; - - /** - * The folder for the 'personal toolbar' root. - */ - readonly attribute fuelIBookmarkFolder toolbar; - - /** - * The folder for the 'tags' root. - */ - readonly attribute fuelIBookmarkFolder tags; - - /** - * The folder for the 'unfiled bookmarks' root. - */ - readonly attribute fuelIBookmarkFolder unfiled; -}; - -/** - * Interface representing a browser window. - */ -[scriptable, uuid(207edb28-eb5e-424e-a862-b0e97C8de866)] -interface fuelIWindow : nsISupports -{ - /** - * A collection of browser tabs within the browser window. - */ - readonly attribute nsIVariant tabs; - - /** - * The currently-active tab within the browser window. - */ - readonly attribute fuelIBrowserTab activeTab; - - /** - * Open a new browser tab, pointing to the specified URI. - * @param aURI - * The uri to open the browser tab to - */ - fuelIBrowserTab open(in nsIURI aURI); - - /** - * The events object for the browser window. - * supports: "TabOpen", "TabClose", "TabMove", "TabSelect" - */ - readonly attribute extIEvents events; -}; - -/** - * Interface representing a browser tab. - */ -[scriptable, uuid(3073ceff-777c-41ce-9ace-ab37268147c1)] -interface fuelIBrowserTab : nsISupports -{ - /** - * The current uri of this tab. - */ - readonly attribute nsIURI uri; - - /** - * The current index of this tab in the browser window. - */ - readonly attribute int32_t index; - - /** - * The browser window that is holding the tab. - */ - readonly attribute fuelIWindow window; - - /** - * The content document of the browser tab. - */ - readonly attribute nsIDOMHTMLDocument document; - - /** - * The events object for the browser tab. - * supports: "load" - */ - readonly attribute extIEvents events; - - /** - * Load a new URI into this browser tab. - * @param aURI - * The uri to load into the browser tab - */ - void load(in nsIURI aURI); - - /** - * Give focus to this browser tab, and bring it to the front. - */ - void focus(); - - /** - * Close the browser tab. This may not actually close the tab - * as script may abort the close operation. - */ - void close(); - - /** - * Moves this browser tab before another browser tab within the window. - * @param aBefore - * The tab before which the target tab will be moved - */ - void moveBefore(in fuelIBrowserTab aBefore); - - /** - * Move this browser tab to the last tab within the window. - */ - void moveToEnd(); -}; - -/** - * Interface for managing and accessing the applications systems - */ -[scriptable, uuid(fe74cf80-aa2d-11db-abbd-0800200c9a66)] -interface fuelIApplication : extIApplication -{ - /** - * The root bookmarks object for the application. - * Contains all the bookmark roots in the system. - */ - readonly attribute fuelIBookmarkRoots bookmarks; - - /** - * An array of browser windows within the application. - */ - readonly attribute nsIVariant windows; - - /** - * The currently active browser window. - */ - readonly attribute fuelIWindow activeWindow; -}; diff --git a/components/fuel/moz.build b/components/fuel/moz.build deleted file mode 100644 index 5c468f2..0000000 --- a/components/fuel/moz.build +++ /dev/null @@ -1,14 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -XPIDL_SOURCES += ['fuelIApplication.idl'] - -XPIDL_MODULE = 'fuel' - -EXTRA_COMPONENTS += ['fuelApplication.manifest'] - -EXTRA_PP_COMPONENTS += ['fuelApplication.js'] - diff --git a/components/moz.build b/components/moz.build deleted file mode 100644 index 48d4552..0000000 --- a/components/moz.build +++ /dev/null @@ -1,47 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -DIRS += [ - 'abouthome', - 'certerror', - 'dirprovider', - 'downloads', - 'feeds', - 'fuel', - 'newtab', - 'pageinfo', - 'places', - 'permissions', - 'preferences', - 'privatebrowsing', - 'search', - 'sessionstore', - 'shell', - 'statusbar', -] - -if CONFIG['MOZ_SERVICES_SYNC']: - DIRS += ['sync'] - -DIRS += ['build'] - -XPIDL_SOURCES += [ - 'nsIBrowserGlue.idl', - 'nsIBrowserHandler.idl', -] - -XPIDL_MODULE = 'browsercompsbase' - -EXTRA_PP_COMPONENTS += [ - 'BrowserComponents.manifest', - 'nsAboutRedirector.js', - 'nsBrowserContentHandler.js', - 'nsBrowserGlue.js', -] - -EXTRA_JS_MODULES += [ - 'distribution.js', -]
\ No newline at end of file diff --git a/components/newtab/cells.js b/components/newtab/cells.js deleted file mode 100644 index 47d4ef5..0000000 --- a/components/newtab/cells.js +++ /dev/null @@ -1,126 +0,0 @@ -#ifdef 0 -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ -#endif - -/** - * This class manages a cell's DOM node (not the actually cell content, a site). - * It's mostly read-only, i.e. all manipulation of both position and content - * aren't handled here. - */ -function Cell(aGrid, aNode) { - this._grid = aGrid; - this._node = aNode; - this._node._newtabCell = this; - - // Register drag-and-drop event handlers. - ["dragenter", "dragover", "dragexit", "drop"].forEach(function (aType) { - this._node.addEventListener(aType, this, false); - }, this); -} - -Cell.prototype = { - /** - * The grid. - */ - _grid: null, - - /** - * The cell's DOM node. - */ - get node() { return this._node; }, - - /** - * The cell's offset in the grid. - */ - get index() { - let index = this._grid.cells.indexOf(this); - - // Cache this value, overwrite the getter. - Object.defineProperty(this, "index", {value: index, enumerable: true}); - - return index; - }, - - /** - * The previous cell in the grid. - */ - get previousSibling() { - let prev = this.node.previousElementSibling; - prev = prev && prev._newtabCell; - - // Cache this value, overwrite the getter. - Object.defineProperty(this, "previousSibling", {value: prev, enumerable: true}); - - return prev; - }, - - /** - * The next cell in the grid. - */ - get nextSibling() { - let next = this.node.nextElementSibling; - next = next && next._newtabCell; - - // Cache this value, overwrite the getter. - Object.defineProperty(this, "nextSibling", {value: next, enumerable: true}); - - return next; - }, - - /** - * The site contained in the cell, if any. - */ - get site() { - let firstChild = this.node.firstElementChild; - return firstChild && firstChild._newtabSite; - }, - - /** - * Checks whether the cell contains a pinned site. - * @return Whether the cell contains a pinned site. - */ - containsPinnedSite: function Cell_containsPinnedSite() { - let site = this.site; - return site && site.isPinned(); - }, - - /** - * Checks whether the cell contains a site (is empty). - * @return Whether the cell is empty. - */ - isEmpty: function Cell_isEmpty() { - return !this.site; - }, - - /** - * Handles all cell events. - */ - handleEvent: function Cell_handleEvent(aEvent) { - // We're not responding to external drag/drop events - // when our parent window is in private browsing mode. - if (inPrivateBrowsingMode() && !gDrag.draggedSite) - return; - - if (aEvent.type != "dragexit" && !gDrag.isValid(aEvent)) - return; - - switch (aEvent.type) { - case "dragenter": - aEvent.preventDefault(); - gDrop.enter(this, aEvent); - break; - case "dragover": - aEvent.preventDefault(); - break; - case "dragexit": - gDrop.exit(this, aEvent); - break; - case "drop": - aEvent.preventDefault(); - gDrop.drop(this, aEvent); - break; - } - } -}; diff --git a/components/newtab/drag.js b/components/newtab/drag.js deleted file mode 100644 index e3928eb..0000000 --- a/components/newtab/drag.js +++ /dev/null @@ -1,151 +0,0 @@ -#ifdef 0 -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ -#endif - -/** - * This singleton implements site dragging functionality. - */ -var gDrag = { - /** - * The site offset to the drag start point. - */ - _offsetX: null, - _offsetY: null, - - /** - * The site that is dragged. - */ - _draggedSite: null, - get draggedSite() { return this._draggedSite; }, - - /** - * The cell width/height at the point the drag started. - */ - _cellWidth: null, - _cellHeight: null, - get cellWidth() { return this._cellWidth; }, - get cellHeight() { return this._cellHeight; }, - - /** - * Start a new drag operation. - * @param aSite The site that's being dragged. - * @param aEvent The 'dragstart' event. - */ - start: function Drag_start(aSite, aEvent) { - this._draggedSite = aSite; - - // Mark nodes as being dragged. - let selector = ".newtab-site, .newtab-control, .newtab-thumbnail"; - let parentCell = aSite.node.parentNode; - let nodes = parentCell.querySelectorAll(selector); - for (let i = 0; i < nodes.length; i++) - nodes[i].setAttribute("dragged", "true"); - - parentCell.setAttribute("dragged", "true"); - - this._setDragData(aSite, aEvent); - - // Store the cursor offset. - let node = aSite.node; - let rect = node.getBoundingClientRect(); - this._offsetX = aEvent.clientX - rect.left; - this._offsetY = aEvent.clientY - rect.top; - - // Store the cell dimensions. - let cellNode = aSite.cell.node; - this._cellWidth = cellNode.offsetWidth; - this._cellHeight = cellNode.offsetHeight; - - gTransformation.freezeSitePosition(aSite); - }, - - /** - * Handles the 'drag' event. - * @param aSite The site that's being dragged. - * @param aEvent The 'drag' event. - */ - drag: function Drag_drag(aSite, aEvent) { - // Get the viewport size. - let {clientWidth, clientHeight} = document.documentElement; - - // We'll want a padding of 5px. - let border = 5; - - // Enforce minimum constraints to keep the drag image inside the window. - let left = Math.max(scrollX + aEvent.clientX - this._offsetX, border); - let top = Math.max(scrollY + aEvent.clientY - this._offsetY, border); - - // Enforce maximum constraints to keep the drag image inside the window. - left = Math.min(left, scrollX + clientWidth - this.cellWidth - border); - top = Math.min(top, scrollY + clientHeight - this.cellHeight - border); - - // Update the drag image's position. - gTransformation.setSitePosition(aSite, {left: left, top: top}); - }, - - /** - * Ends the current drag operation. - * @param aSite The site that's being dragged. - * @param aEvent The 'dragend' event. - */ - end: function Drag_end(aSite, aEvent) { - let nodes = gGrid.node.querySelectorAll("[dragged]") - for (let i = 0; i < nodes.length; i++) - nodes[i].removeAttribute("dragged"); - - // Slide the dragged site back into its cell (may be the old or the new cell). - gTransformation.slideSiteTo(aSite, aSite.cell, {unfreeze: true}); - - this._draggedSite = null; - }, - - /** - * Checks whether we're responsible for a given drag event. - * @param aEvent The drag event to check. - * @return Whether we should handle this drag and drop operation. - */ - isValid: function Drag_isValid(aEvent) { - let link = gDragDataHelper.getLinkFromDragEvent(aEvent); - - // Check that the drag data is non-empty. - // Can happen when dragging places folders. - if (!link || !link.url) { - return false; - } - - // Check that we're not accepting URLs which would inherit the caller's - // principal (such as javascript: or data:). - return gLinkChecker.checkLoadURI(link.url); - }, - - /** - * Initializes the drag data for the current drag operation. - * @param aSite The site that's being dragged. - * @param aEvent The 'dragstart' event. - */ - _setDragData: function Drag_setDragData(aSite, aEvent) { - let {url, title} = aSite; - - let dt = aEvent.dataTransfer; - dt.mozCursor = "default"; - dt.effectAllowed = "move"; - dt.setData("text/plain", url); - dt.setData("text/uri-list", url); - dt.setData("text/x-moz-url", url + "\n" + title); - dt.setData("text/html", "<a href=\"" + url + "\">" + url + "</a>"); - - // Create and use an empty drag element. We don't want to use the default - // drag image with its default opacity. - let dragElement = document.createElementNS(HTML_NAMESPACE, "div"); - dragElement.classList.add("newtab-drag"); - let scrollbox = document.getElementById("newtab-vertical-margin"); - scrollbox.appendChild(dragElement); - dt.setDragImage(dragElement, 0, 0); - - // After the 'dragstart' event has been processed we can remove the - // temporary drag element from the DOM. - setTimeout(() => scrollbox.removeChild(dragElement), 0); - } -}; diff --git a/components/newtab/dragDataHelper.js b/components/newtab/dragDataHelper.js deleted file mode 100644 index 675ff26..0000000 --- a/components/newtab/dragDataHelper.js +++ /dev/null @@ -1,22 +0,0 @@ -#ifdef 0 -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ -#endif - -var gDragDataHelper = { - get mimeType() { - return "text/x-moz-url"; - }, - - getLinkFromDragEvent: function DragDataHelper_getLinkFromDragEvent(aEvent) { - let dt = aEvent.dataTransfer; - if (!dt || !dt.types.includes(this.mimeType)) { - return null; - } - - let data = dt.getData(this.mimeType) || ""; - let [url, title] = data.split(/[\r\n]+/); - return {url: url, title: title}; - } -}; diff --git a/components/newtab/drop.js b/components/newtab/drop.js deleted file mode 100644 index 7486524..0000000 --- a/components/newtab/drop.js +++ /dev/null @@ -1,150 +0,0 @@ -#ifdef 0 -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ -#endif - -// A little delay that prevents the grid from being too sensitive when dragging -// sites around. -const DELAY_REARRANGE_MS = 100; - -/** - * This singleton implements site dropping functionality. - */ -var gDrop = { - /** - * The last drop target. - */ - _lastDropTarget: null, - - /** - * Handles the 'dragenter' event. - * @param aCell The drop target cell. - */ - enter: function Drop_enter(aCell) { - this._delayedRearrange(aCell); - }, - - /** - * Handles the 'dragexit' event. - * @param aCell The drop target cell. - * @param aEvent The 'dragexit' event. - */ - exit: function Drop_exit(aCell, aEvent) { - if (aEvent.dataTransfer && !aEvent.dataTransfer.mozUserCancelled) { - this._delayedRearrange(); - } else { - // The drag operation has been cancelled. - this._cancelDelayedArrange(); - this._rearrange(); - } - }, - - /** - * Handles the 'drop' event. - * @param aCell The drop target cell. - * @param aEvent The 'dragexit' event. - */ - drop: function Drop_drop(aCell, aEvent) { - // The cell that is the drop target could contain a pinned site. We need - // to find out where that site has gone and re-pin it there. - if (aCell.containsPinnedSite()) - this._repinSitesAfterDrop(aCell); - - // Pin the dragged or insert the new site. - this._pinDraggedSite(aCell, aEvent); - - this._cancelDelayedArrange(); - - // Update the grid and move all sites to their new places. - gUpdater.updateGrid(); - }, - - /** - * Re-pins all pinned sites in their (new) positions. - * @param aCell The drop target cell. - */ - _repinSitesAfterDrop: function Drop_repinSitesAfterDrop(aCell) { - let sites = gDropPreview.rearrange(aCell); - - // Filter out pinned sites. - let pinnedSites = sites.filter(function (aSite) { - return aSite && aSite.isPinned(); - }); - - // Re-pin all shifted pinned cells. - pinnedSites.forEach(aSite => aSite.pin(sites.indexOf(aSite))); - }, - - /** - * Pins the dragged site in its new place. - * @param aCell The drop target cell. - * @param aEvent The 'dragexit' event. - */ - _pinDraggedSite: function Drop_pinDraggedSite(aCell, aEvent) { - let index = aCell.index; - let draggedSite = gDrag.draggedSite; - - if (draggedSite) { - // Pin the dragged site at its new place. - if (aCell != draggedSite.cell) - draggedSite.pin(index); - } else { - let link = gDragDataHelper.getLinkFromDragEvent(aEvent); - if (link) { - // A new link was dragged onto the grid. Create it by pinning its URL. - gPinnedLinks.pin(link, index); - - // Make sure the newly added link is not blocked. - gBlockedLinks.unblock(link); - } - } - }, - - /** - * Time a rearrange with a little delay. - * @param aCell The drop target cell. - */ - _delayedRearrange: function Drop_delayedRearrange(aCell) { - // The last drop target didn't change so there's no need to re-arrange. - if (this._lastDropTarget == aCell) - return; - - let self = this; - - function callback() { - self._rearrangeTimeout = null; - self._rearrange(aCell); - } - - this._cancelDelayedArrange(); - this._rearrangeTimeout = setTimeout(callback, DELAY_REARRANGE_MS); - - // Store the last drop target. - this._lastDropTarget = aCell; - }, - - /** - * Cancels a timed rearrange, if any. - */ - _cancelDelayedArrange: function Drop_cancelDelayedArrange() { - if (this._rearrangeTimeout) { - clearTimeout(this._rearrangeTimeout); - this._rearrangeTimeout = null; - } - }, - - /** - * Rearrange all sites in the grid depending on the current drop target. - * @param aCell The drop target cell. - */ - _rearrange: function Drop_rearrange(aCell) { - let sites = gGrid.sites; - - // We need to rearrange the grid only if there's a current drop target. - if (aCell) - sites = gDropPreview.rearrange(aCell); - - gTransformation.rearrangeSites(sites, {unfreeze: !aCell}); - } -}; diff --git a/components/newtab/dropPreview.js b/components/newtab/dropPreview.js deleted file mode 100644 index fd7587a..0000000 --- a/components/newtab/dropPreview.js +++ /dev/null @@ -1,222 +0,0 @@ -#ifdef 0 -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ -#endif - -/** - * This singleton provides the ability to re-arrange the current grid to - * indicate the transformation that results from dropping a cell at a certain - * position. - */ -var gDropPreview = { - /** - * Rearranges the sites currently contained in the grid when a site would be - * dropped onto the given cell. - * @param aCell The drop target cell. - * @return The re-arranged array of sites. - */ - rearrange: function DropPreview_rearrange(aCell) { - let sites = gGrid.sites; - - // Insert the dragged site into the current grid. - this._insertDraggedSite(sites, aCell); - - // After the new site has been inserted we need to correct the positions - // of all pinned tabs that have been moved around. - this._repositionPinnedSites(sites, aCell); - - return sites; - }, - - /** - * Inserts the currently dragged site into the given array of sites. - * @param aSites The array of sites to insert into. - * @param aCell The drop target cell. - */ - _insertDraggedSite: function DropPreview_insertDraggedSite(aSites, aCell) { - let dropIndex = aCell.index; - let draggedSite = gDrag.draggedSite; - - // We're currently dragging a site. - if (draggedSite) { - let dragCell = draggedSite.cell; - let dragIndex = dragCell.index; - - // Move the dragged site into its new position. - if (dragIndex != dropIndex) { - aSites.splice(dragIndex, 1); - aSites.splice(dropIndex, 0, draggedSite); - } - // We're handling an external drag item. - } else { - aSites.splice(dropIndex, 0, null); - } - }, - - /** - * Correct the position of all pinned sites that might have been moved to - * different positions after the dragged site has been inserted. - * @param aSites The array of sites containing the dragged site. - * @param aCell The drop target cell. - */ - _repositionPinnedSites: - function DropPreview_repositionPinnedSites(aSites, aCell) { - - // Collect all pinned sites. - let pinnedSites = this._filterPinnedSites(aSites, aCell); - - // Correct pinned site positions. - pinnedSites.forEach(function (aSite) { - aSites[aSites.indexOf(aSite)] = aSites[aSite.cell.index]; - aSites[aSite.cell.index] = aSite; - }, this); - - // There might be a pinned cell that got pushed out of the grid, try to - // sneak it in by removing a lower-priority cell. - if (this._hasOverflowedPinnedSite(aSites, aCell)) - this._repositionOverflowedPinnedSite(aSites, aCell); - }, - - /** - * Filter pinned sites out of the grid that are still on their old positions - * and have not moved. - * @param aSites The array of sites to filter. - * @param aCell The drop target cell. - * @return The filtered array of sites. - */ - _filterPinnedSites: function DropPreview_filterPinnedSites(aSites, aCell) { - let draggedSite = gDrag.draggedSite; - - // When dropping on a cell that contains a pinned site make sure that all - // pinned cells surrounding the drop target are moved as well. - let range = this._getPinnedRange(aCell); - - return aSites.filter(function (aSite, aIndex) { - // The site must be valid, pinned and not the dragged site. - if (!aSite || aSite == draggedSite || !aSite.isPinned()) - return false; - - let index = aSite.cell.index; - - // If it's not in the 'pinned range' it's a valid pinned site. - return (index > range.end || index < range.start); - }); - }, - - /** - * Determines the range of pinned sites surrounding the drop target cell. - * @param aCell The drop target cell. - * @return The range of pinned cells. - */ - _getPinnedRange: function DropPreview_getPinnedRange(aCell) { - let dropIndex = aCell.index; - let range = {start: dropIndex, end: dropIndex}; - - // We need a pinned range only when dropping on a pinned site. - if (aCell.containsPinnedSite()) { - let links = gPinnedLinks.links; - - // Find all previous siblings of the drop target that are pinned as well. - while (range.start && links[range.start - 1]) - range.start--; - - let maxEnd = links.length - 1; - - // Find all next siblings of the drop target that are pinned as well. - while (range.end < maxEnd && links[range.end + 1]) - range.end++; - } - - return range; - }, - - /** - * Checks if the given array of sites contains a pinned site that has - * been pushed out of the grid. - * @param aSites The array of sites to check. - * @param aCell The drop target cell. - * @return Whether there is an overflowed pinned cell. - */ - _hasOverflowedPinnedSite: - function DropPreview_hasOverflowedPinnedSite(aSites, aCell) { - - // If the drop target isn't pinned there's no way a pinned site has been - // pushed out of the grid so we can just exit here. - if (!aCell.containsPinnedSite()) - return false; - - let cells = gGrid.cells; - - // No cells have been pushed out of the grid, nothing to do here. - if (aSites.length <= cells.length) - return false; - - let overflowedSite = aSites[cells.length]; - - // Nothing to do if the site that got pushed out of the grid is not pinned. - return (overflowedSite && overflowedSite.isPinned()); - }, - - /** - * We have a overflowed pinned site that we need to re-position so that it's - * visible again. We try to find a lower-priority cell (empty or containing - * an unpinned site) that we can move it to. - * @param aSites The array of sites. - * @param aCell The drop target cell. - */ - _repositionOverflowedPinnedSite: - function DropPreview_repositionOverflowedPinnedSite(aSites, aCell) { - - // Try to find a lower-priority cell (empty or containing an unpinned site). - let index = this._indexOfLowerPrioritySite(aSites, aCell); - - if (index > -1) { - let cells = gGrid.cells; - let dropIndex = aCell.index; - - // Move all pinned cells to their new positions to let the overflowed - // site fit into the grid. - for (let i = index + 1, lastPosition = index; i < aSites.length; i++) { - if (i != dropIndex) { - aSites[lastPosition] = aSites[i]; - lastPosition = i; - } - } - - // Finally, remove the overflowed site from its previous position. - aSites.splice(cells.length, 1); - } - }, - - /** - * Finds the index of the last cell that is empty or contains an unpinned - * site. These are considered to be of a lower priority. - * @param aSites The array of sites. - * @param aCell The drop target cell. - * @return The cell's index. - */ - _indexOfLowerPrioritySite: - function DropPreview_indexOfLowerPrioritySite(aSites, aCell) { - - let cells = gGrid.cells; - let dropIndex = aCell.index; - - // Search (beginning with the last site in the grid) for a site that is - // empty or unpinned (an thus lower-priority) and can be pushed out of the - // grid instead of the pinned site. - for (let i = cells.length - 1; i >= 0; i--) { - // The cell that is our drop target is not a good choice. - if (i == dropIndex) - continue; - - let site = aSites[i]; - - // We can use the cell only if it's empty or the site is un-pinned. - if (!site || !site.isPinned()) - return i; - } - - return -1; - } -}; diff --git a/components/newtab/dropTargetShim.js b/components/newtab/dropTargetShim.js deleted file mode 100644 index 57a97fa..0000000 --- a/components/newtab/dropTargetShim.js +++ /dev/null @@ -1,232 +0,0 @@ -#ifdef 0 -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ -#endif - -/** - * This singleton provides a custom drop target detection. We need this because - * the default DnD target detection relies on the cursor's position. We want - * to pick a drop target based on the dragged site's position. - */ -var gDropTargetShim = { - /** - * Cache for the position of all cells, cleaned after drag finished. - */ - _cellPositions: null, - - /** - * The last drop target that was hovered. - */ - _lastDropTarget: null, - - /** - * Initializes the drop target shim. - */ - init: function () { - gGrid.node.addEventListener("dragstart", this, true); - }, - - /** - * Add all event listeners needed during a drag operation. - */ - _addEventListeners: function () { - gGrid.node.addEventListener("dragend", this); - - let docElement = document.documentElement; - docElement.addEventListener("dragover", this); - docElement.addEventListener("dragenter", this); - docElement.addEventListener("drop", this); - }, - - /** - * Remove all event listeners that were needed during a drag operation. - */ - _removeEventListeners: function () { - gGrid.node.removeEventListener("dragend", this); - - let docElement = document.documentElement; - docElement.removeEventListener("dragover", this); - docElement.removeEventListener("dragenter", this); - docElement.removeEventListener("drop", this); - }, - - /** - * Handles all shim events. - */ - handleEvent: function (aEvent) { - switch (aEvent.type) { - case "dragstart": - this._dragstart(aEvent); - break; - case "dragenter": - aEvent.preventDefault(); - break; - case "dragover": - this._dragover(aEvent); - break; - case "drop": - this._drop(aEvent); - break; - case "dragend": - this._dragend(aEvent); - break; - } - }, - - /** - * Handles the 'dragstart' event. - * @param aEvent The 'dragstart' event. - */ - _dragstart: function (aEvent) { - if (aEvent.target.classList.contains("newtab-link")) { - gGrid.lock(); - this._addEventListeners(); - } - }, - - /** - * Handles the 'dragover' event. - * @param aEvent The 'dragover' event. - */ - _dragover: function (aEvent) { - // XXX bug 505521 - Use the dragover event to retrieve the - // current mouse coordinates while dragging. - let sourceNode = aEvent.dataTransfer.mozSourceNode.parentNode; - gDrag.drag(sourceNode._newtabSite, aEvent); - - // Find the current drop target, if there's one. - this._updateDropTarget(aEvent); - - // If we have a valid drop target, - // let the drag-and-drop service know. - if (this._lastDropTarget) { - aEvent.preventDefault(); - } - }, - - /** - * Handles the 'drop' event. - * @param aEvent The 'drop' event. - */ - _drop: function (aEvent) { - // We're accepting all drops. - aEvent.preventDefault(); - - // remember that drop event was seen, this explicitly - // assumes that drop event preceeds dragend event - this._dropSeen = true; - - // Make sure to determine the current drop target - // in case the dragover event hasn't been fired. - this._updateDropTarget(aEvent); - - // A site was successfully dropped. - this._dispatchEvent(aEvent, "drop", this._lastDropTarget); - }, - - /** - * Handles the 'dragend' event. - * @param aEvent The 'dragend' event. - */ - _dragend: function (aEvent) { - if (this._lastDropTarget) { - if (aEvent.dataTransfer.mozUserCancelled || !this._dropSeen) { - // The drag operation was cancelled or no drop event was generated - this._dispatchEvent(aEvent, "dragexit", this._lastDropTarget); - this._dispatchEvent(aEvent, "dragleave", this._lastDropTarget); - } - - // Clean up. - this._lastDropTarget = null; - this._cellPositions = null; - } - - this._dropSeen = false; - gGrid.unlock(); - this._removeEventListeners(); - }, - - /** - * Tries to find the current drop target and will fire - * appropriate dragenter, dragexit, and dragleave events. - * @param aEvent The current drag event. - */ - _updateDropTarget: function (aEvent) { - // Let's see if we find a drop target. - let target = this._findDropTarget(aEvent); - - if (target != this._lastDropTarget) { - if (this._lastDropTarget) - // We left the last drop target. - this._dispatchEvent(aEvent, "dragexit", this._lastDropTarget); - - if (target) - // We're now hovering a (new) drop target. - this._dispatchEvent(aEvent, "dragenter", target); - - if (this._lastDropTarget) - // We left the last drop target. - this._dispatchEvent(aEvent, "dragleave", this._lastDropTarget); - - this._lastDropTarget = target; - } - }, - - /** - * Determines the current drop target by matching the dragged site's position - * against all cells in the grid. - * @return The currently hovered drop target or null. - */ - _findDropTarget: function () { - // These are the minimum intersection values - we want to use the cell if - // the site is >= 50% hovering its position. - let minWidth = gDrag.cellWidth / 2; - let minHeight = gDrag.cellHeight / 2; - - let cellPositions = this._getCellPositions(); - let rect = gTransformation.getNodePosition(gDrag.draggedSite.node); - - // Compare each cell's position to the dragged site's position. - for (let i = 0; i < cellPositions.length; i++) { - let inter = rect.intersect(cellPositions[i].rect); - - // If the intersection is big enough we found a drop target. - if (inter.width >= minWidth && inter.height >= minHeight) - return cellPositions[i].cell; - } - - // No drop target found. - return null; - }, - - /** - * Gets the positions of all cell nodes. - * @return The (cached) cell positions. - */ - _getCellPositions: function DropTargetShim_getCellPositions() { - if (this._cellPositions) - return this._cellPositions; - - return this._cellPositions = gGrid.cells.map(function (cell) { - return {cell: cell, rect: gTransformation.getNodePosition(cell.node)}; - }); - }, - - /** - * Dispatches a custom DragEvent on the given target node. - * @param aEvent The source event. - * @param aType The event type. - * @param aTarget The target node that receives the event. - */ - _dispatchEvent: function (aEvent, aType, aTarget) { - let node = aTarget.node; - let event = document.createEvent("DragEvent"); - - // The event should not bubble to prevent recursion. - event.initDragEvent(aType, false, true, window, 0, 0, 0, 0, 0, false, false, - false, false, 0, node, aEvent.dataTransfer); - - node.dispatchEvent(event); - } -}; diff --git a/components/newtab/grid.js b/components/newtab/grid.js deleted file mode 100644 index e63ea54..0000000 --- a/components/newtab/grid.js +++ /dev/null @@ -1,175 +0,0 @@ -#ifdef 0 -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ -#endif - -/** - * This singleton represents the grid that contains all sites. - */ -var gGrid = { - /** - * The DOM node of the grid. - */ - _node: null, - _gridDefaultContent: null, - get node() { return this._node; }, - - /** - * The cached DOM fragment for sites. - */ - _siteFragment: null, - - /** - * All cells contained in the grid. - */ - _cells: [], - get cells() { return this._cells; }, - - /** - * All sites contained in the grid's cells. Sites may be empty. - */ - get sites() { - // return [for (cell of this.cells) cell.site]; - let aSites = []; - for (let cell of this.cells) { - aSites.push(cell.site); - } - return aSites; - }, - - // Tells whether the grid has already been initialized. - get ready() { return !!this._ready; }, - - // Returns whether the page has finished loading yet. - get isDocumentLoaded() { return document.readyState == "complete"; }, - - /** - * Initializes the grid. - * @param aSelector The query selector of the grid. - */ - init: function Grid_init() { - this._node = document.getElementById("newtab-grid"); - this._gridDefaultContent = this._node.lastChild; - this._createSiteFragment(); - - gLinks.populateCache(() => { - this._refreshGrid(); - this._ready = true; - }); - }, - - /** - * Creates a new site in the grid. - * @param aLink The new site's link. - * @param aCell The cell that will contain the new site. - * @return The newly created site. - */ - createSite: function Grid_createSite(aLink, aCell) { - let node = aCell.node; - node.appendChild(this._siteFragment.cloneNode(true)); - return new Site(node.firstElementChild, aLink); - }, - - /** - * Handles all grid events. - */ - handleEvent: function Grid_handleEvent(aEvent) { - // Any specific events should go here. - }, - - /** - * Locks the grid to block all pointer events. - */ - lock: function Grid_lock() { - this.node.setAttribute("locked", "true"); - }, - - /** - * Unlocks the grid to allow all pointer events. - */ - unlock: function Grid_unlock() { - this.node.removeAttribute("locked"); - }, - - /** - * Renders the grid. - */ - refresh() { - this._refreshGrid(); - }, - - /** - * Renders the grid, including cells and sites. - */ - _refreshGrid() { - let row = document.createElementNS(HTML_NAMESPACE, "div"); - row.classList.add("newtab-row"); - let cell = document.createElementNS(HTML_NAMESPACE, "div"); - cell.classList.add("newtab-cell"); - - // Clear the grid - this._node.innerHTML = ""; - - // Creates the structure of one row - for (let i = 0; i < gGridPrefs.gridColumns; i++) { - row.appendChild(cell.cloneNode(true)); - } - - // Creates the grid - for (let j = 0; j < gGridPrefs.gridRows; j++) { - this._node.appendChild(row.cloneNode(true)); - } - - // Create cell array. - let cellElements = this.node.querySelectorAll(".newtab-cell"); - let cells = Array.from(cellElements, (cell) => new Cell(this, cell)); - - // Fetch links. - let links = gLinks.getLinks(); - - // Create sites. - let numLinks = Math.min(links.length, cells.length); - for (let i = 0; i < numLinks; i++) { - if (links[i]) { - this.createSite(links[i], cells[i]); - } - } - - this._cells = cells; - }, - - /** - * Creates the DOM fragment that is re-used when creating sites. - */ - _createSiteFragment: function Grid_createSiteFragment() { - let site = document.createElementNS(HTML_NAMESPACE, "div"); - site.classList.add("newtab-site"); - site.setAttribute("draggable", "true"); - - // Create the site's inner HTML code. - site.innerHTML = - '<a class="newtab-link">' + - ' <span class="newtab-thumbnail placeholder"/>' + - ' <span class="newtab-thumbnail thumbnail"/>' + - ' <span class="newtab-title"/>' + - '</a>' + - '<input type="button" title="' + newTabString("pin") + '"' + - ' class="newtab-control newtab-control-pin"/>' + - '<input type="button" title="' + newTabString("block") + '"' + - ' class="newtab-control newtab-control-block"/>'; - - this._siteFragment = document.createDocumentFragment(); - this._siteFragment.appendChild(site); - }, - - /** - * Test a tile at a given position for being pinned or history - * @param position Position in sites array - */ - _isHistoricalTile: function Grid_isHistoricalTile(aPos) { - let site = this.sites[aPos]; - return site && (site.isPinned() || site.link && site.link.type == "history"); - } - -}; diff --git a/components/newtab/jar.mn b/components/newtab/jar.mn deleted file mode 100644 index 2d62914..0000000 --- a/components/newtab/jar.mn +++ /dev/null @@ -1,8 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -browser.jar: - content/browser/newtab/newTab.xhtml -* content/browser/newtab/newTab.js - content/browser/newtab/newTab.css
\ No newline at end of file diff --git a/components/newtab/moz.build b/components/newtab/moz.build deleted file mode 100644 index 2d64d50..0000000 --- a/components/newtab/moz.build +++ /dev/null @@ -1,8 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -JAR_MANIFESTS += ['jar.mn'] - diff --git a/components/newtab/newTab.css b/components/newtab/newTab.css deleted file mode 100644 index 3c7cfa1..0000000 --- a/components/newtab/newTab.css +++ /dev/null @@ -1,349 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -html { - width: 100%; - height: 100%; -} - -body { - font: message-box; - width: 100%; - height: 100%; - padding: 0; - margin: 0; - background-color: #F9F9F9; - display: -moz-box; - position: relative; - -moz-box-flex: 1; - -moz-user-focus: normal; - -moz-box-orient: vertical; -} - -input { - font: message-box; - font-size: 16px; -} - -input[type=button] { - cursor: pointer; -} - -/* UNDO */ -#newtab-undo-container { - transition: opacity 100ms ease-out; - -moz-box-align: center; - -moz-box-pack: center; -} - -#newtab-undo-container[undo-disabled] { - opacity: 0; - pointer-events: none; -} - -/* TOGGLE */ -#newtab-toggle { - position: absolute; - top: 12px; - right: 12px; -} - -#newtab-toggle:-moz-locale-dir(rtl) { - left: 12px; - right: auto; -} - -/* MARGINS */ -#newtab-vertical-margin { - display: -moz-box; - position: relative; - -moz-box-flex: 1; - -moz-box-orient: vertical; -} - -#newtab-margin-undo-container { - display: -moz-box; - left: 6px; - position: absolute; - top: 6px; - z-index: 1; -} - -#newtab-margin-undo-container:dir(rtl) { - left: auto; - right: 6px; -} - -#newtab-undo-close-button:dir(rtl) { - float:left; -} - -#newtab-horizontal-margin { - display: -moz-box; - -moz-box-flex: 5; -} - -#newtab-margin-top { - min-height: 10px; - max-height: 30px; - display: -moz-box; - -moz-box-flex: 1; - -moz-box-align: center; - -moz-box-pack: center; -} - -#newtab-margin-bottom { - min-height: 40px; - max-height: 80px; - -moz-box-flex: 1; -} - -.newtab-side-margin { - min-width: 40px; - max-width: 300px; - -moz-box-flex: 1; -} - -/* GRID */ -#newtab-grid { - display: -moz-box; - -moz-box-flex: 5; - -moz-box-orient: vertical; - min-width: 600px; - min-height: 400px; - transition: 175ms ease-out; - transition-property: opacity; -} - -#newtab-grid[page-disabled] { - opacity: 0; -} - -#newtab-grid[locked], -#newtab-grid[page-disabled] { - pointer-events: none; -} - -/* ROWS */ -.newtab-row { - display: -moz-box; - -moz-box-orient: horizontal; - -moz-box-direction: normal; - -moz-box-flex: 1; -} - -/* - * Thumbnail image sizes are determined in the preferences: - * toolkit.pageThumbs.minWidth - * toolkit.pageThumbs.minHeight - */ -/* CELLS */ -.newtab-cell { - display: -moz-box; - -moz-box-flex: 1; -} - -/* SITES */ -.newtab-site { - position: relative; - -moz-box-flex: 1; - transition: 150ms ease-out; - transition-property: top, left, opacity; -} - -.newtab-site[frozen] { - position: absolute; - pointer-events: none; -} - -.newtab-site[dragged] { - transition-property: none; - z-index: 10; -} - -/* LINK + THUMBNAILS */ -.newtab-link, -.newtab-thumbnail { - position: absolute; - left: 0; - top: 0; - right: 0; - bottom: 0; -} - -/* TITLES */ -.newtab-title { - overflow: hidden; - position: absolute; - right: 0; - text-align: center; -} - -.newtab-title { - bottom: 0; - white-space: nowrap; - text-overflow: ellipsis; - vertical-align: middle; -} - -.newtab-title { - left: 0; - padding: 0 4px; -} - -/* CONTROLS */ -.newtab-control { - position: absolute; - opacity: 0; - transition: opacity 100ms ease-out; -} - -.newtab-control:-moz-focusring, -.newtab-cell:not([ignorehover]) > .newtab-site:hover > .newtab-control { - opacity: 1; -} - -.newtab-control[dragged] { - opacity: 0 !important; -} - -@media (-moz-touch-enabled) { - .newtab-control { - opacity: 1; - } -} - -/* DRAG & DROP */ - -/* - * This is just a temporary drag element used for dataTransfer.setDragImage() - * so that we can use custom drag images and elements. It needs an opacity of - * 0.01 so that the core code detects that it's in fact a visible element. - */ -.newtab-drag { - width: 1px; - height: 1px; - background-color: #fff; - opacity: 0.01; -} - -/* SEARCH */ -#searchContainer { - display: -moz-box; - position: relative; - -moz-box-pack: center; - margin: 10px 0 15px; -} - -#searchContainer[page-disabled] { - opacity: 0; - pointer-events: none; -} - -#searchForm { - display: -moz-box; - position: relative; - height: 36px; - -moz-box-flex: 1; - max-width: 600px; /* 2 * (290 cell width + 10 cell margin) */ -} - -#searchEngineLogo { - border: 1px transparent; - padding: 2px 4px; - margin: 0; - width: 32px; - height: 32px; - position: absolute; -} - -#searchText { - -moz-box-flex: 1; - padding-top: 6px; - padding-bottom: 6px; - padding-inline-start: 42px; - padding-inline-end: 8px; - background: hsla(0,0%,100%,.9) padding-box; - border: 1px solid; - border-spacing: 0; - border-radius: 2px 0 0 2px; - border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2); - box-shadow: 0 1px 0 hsla(210,65%,9%,.02) inset, - 0 0 2px hsla(210,65%,9%,.1) inset, - 0 1px 0 hsla(0,0%,100%,.2); - color: inherit; - unicode-bidi: plaintext; -} - -#searchText:dir(rtl) { - border-radius: 0 2px 2px 0; -} - -#searchText[aria-expanded="true"] { - border-radius: 2px 0 0 0; -} - -#searchText[aria-expanded="true"]:dir(rtl) { - border-radius: 0 2px 0 0; -} - -#searchText[keepfocus], -#searchText:focus { - border-color: hsla(216,100%,60%,.6) hsla(216,76%,52%,.6) hsla(214,100%,40%,.6); -} - -#searchSubmit { - margin-inline-start: -1px; - padding: 0; - border: 1px solid; - background-color: #e0e0e0; - color: black; - border-color: hsla(220,54%,20%,.15) hsla(220,54%,20%,.17) hsla(220,54%,20%,.2); - border-radius: 0 2px 2px 0; - box-shadow: 0 0 2px hsla(0,0%,100%,.5) inset, - 0 1px 0 hsla(0,0%,100%,.2); - cursor: pointer; - transition-property: background-color, border-color, box-shadow; - transition-duration: 150ms; - width: 50px; -} - -#searchSubmit:dir(rtl) { - border-radius: 2px 0 0 2px; -} - -#searchSubmit:hover { - background-color: hsl(220,54%,20%); - color: white; -} - -#searchText:focus + #searchSubmit, -#searchText + #searchSubmit:hover { - border-color: #5985fc #4573e7 #3264d5; -} - -#searchText:focus + #searchSubmit, -#searchText[keepfocus] + #searchSubmit { - box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset, - 0 0 0 1px hsla(0,0%,100%,.1) inset, - 0 1px 0 hsla(220,54%,20%,.03); -} - -#searchText + #searchSubmit:hover { - box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset, - 0 0 0 1px hsla(0,0%,100%,.1) inset, - 0 1px 0 hsla(220,54%,20%,.03), - 0 0 4px hsla(216,100%,20%,.2); -} - -#searchText + #searchSubmit:hover:active { - box-shadow: 0 1px 1px hsla(221,79%,6%,.1) inset, - 0 0 1px hsla(221,79%,6%,.2) inset; - transition-duration: 0ms; -} - -.contentSearchSuggestionTable { - font: message-box; - font-size: 16px; -} diff --git a/components/newtab/newTab.js b/components/newtab/newTab.js deleted file mode 100644 index 0022f21..0000000 --- a/components/newtab/newTab.js +++ /dev/null @@ -1,69 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -var Cu = Components.utils; -var Ci = Components.interfaces; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/PageThumbs.jsm"); -Cu.import("resource://gre/modules/BackgroundPageThumbs.jsm"); -Cu.import("resource://gre/modules/NewTabUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "Rect", - "resource://gre/modules/Geometry.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", - "resource://gre/modules/PrivateBrowsingUtils.jsm"); - -var { - links: gLinks, - allPages: gAllPages, - linkChecker: gLinkChecker, - pinnedLinks: gPinnedLinks, - blockedLinks: gBlockedLinks, - gridPrefs: gGridPrefs -} = NewTabUtils; - -XPCOMUtils.defineLazyGetter(this, "gStringBundle", function() { - return Services.strings. - createBundle("chrome://browser/locale/newTab.properties"); -}); - -function newTabString(name, args) { - let stringName = "newtab." + name; - if (!args) { - return gStringBundle.GetStringFromName(stringName); - } - return gStringBundle.formatStringFromName(stringName, args, args.length); -} - -function inPrivateBrowsingMode() { - return PrivateBrowsingUtils.isContentWindowPrivate(window); -} - -const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml"; -const XUL_NAMESPACE = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; - -const TILES_EXPLAIN_LINK = "https://support.mozilla.org/kb/how-do-tiles-work-firefox"; -const TILES_INTRO_LINK = "https://www.mozilla.org/firefox/tiles/"; -const TILES_PRIVACY_LINK = "https://www.mozilla.org/privacy/"; - -#include transformations.js -#include page.js -#include grid.js -#include cells.js -#include sites.js -#include drag.js -#include dragDataHelper.js -#include drop.js -#include dropTargetShim.js -#include dropPreview.js -#include updater.js -#include undo.js -#include search.js - -// Everything is loaded. Initialize the New Tab Page. -gPage.init(); diff --git a/components/newtab/newTab.xhtml b/components/newtab/newTab.xhtml deleted file mode 100644 index de000e7..0000000 --- a/components/newtab/newTab.xhtml +++ /dev/null @@ -1,61 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<!DOCTYPE html [ - <!ENTITY % newTabDTD SYSTEM "chrome://browser/locale/newTab.dtd"> - %newTabDTD; - <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd"> - %browserDTD; - <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd"> - %globalDTD; -]> - -<html xmlns="http://www.w3.org/1999/xhtml"> -<head> - <title>&newtab.pageTitle;</title> - - <link rel="stylesheet" type="text/css" media="all" href="chrome://global/skin/" /> - <link rel="stylesheet" type="text/css" media="all" href="chrome://browser/content/newtab/newTab.css" /> - <link rel="stylesheet" type="text/css" media="all" href="chrome://browser/skin/newtab/newTab.css" /> -</head> - -<body dir="&locale.dir;"> - <div id="newtab-vertical-margin"> - <div id="newtab-margin-top"/> - - <div id="newtab-margin-undo-container"> - <div id="newtab-undo-container" undo-disabled="true"> - <label id="newtab-undo-label">&newtab.undo.removedLabel;</label> - <button id="newtab-undo-button" tabindex="-1" - class="newtab-undo-button">&newtab.undo.undoButton;</button> - <button id="newtab-undo-restore-button" tabindex="-1" - class="newtab-undo-button">&newtab.undo.restoreButton;</button> - <button id="newtab-undo-close-button" tabindex="-1" title="&newtab.undo.closeTooltip;"/> - </div> - </div> - - <div id="searchContainer"> - <form name="searchForm" id="searchForm" onsubmit="onSearchSubmit(event)"> - <div id="searchLogoContainer"><img id="searchEngineLogo"/></div> - <input type="text" name="q" value="" id="searchText" maxlength="256"/> - <input id="searchSubmit" type="submit" value="&newtab.searchEngineButton.label;"/> - </form> - </div> - - <div id="newtab-horizontal-margin"> - <div class="newtab-side-margin"/> - <div id="newtab-grid"> - <!-- site grid --> - </div> - <div class="newtab-side-margin"/> - </div> - - <div id="newtab-margin-bottom"/> - <input id="newtab-toggle" type="button"/> - </div> -</body> -<script type="text/javascript;version=1.8" src="chrome://browser/content/newtab/newTab.js"/> -</html> diff --git a/components/newtab/page.js b/components/newtab/page.js deleted file mode 100644 index 34387fd..0000000 --- a/components/newtab/page.js +++ /dev/null @@ -1,244 +0,0 @@ -#ifdef 0 -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ -#endif - -// The amount of time we wait while coalescing updates for hidden pages. -const SCHEDULE_UPDATE_TIMEOUT_MS = 1000; - -/** - * This singleton represents the whole 'New Tab Page' and takes care of - * initializing all its components. - */ -var gPage = { - /** - * Initializes the page. - */ - init: function Page_init() { - // Add ourselves to the list of pages to receive notifications. - gAllPages.register(this); - - // Listen for 'unload' to unregister this page. - addEventListener("unload", this, false); - - // Listen for toggle button clicks. - let button = document.getElementById("newtab-toggle"); - button.addEventListener("click", e => this.toggleEnabled(e)); - - // XXX bug 991111 - Not all click events are correctly triggered when - // listening from xhtml nodes -- in particular middle clicks on sites, so - // listen from the xul window and filter then delegate - addEventListener("click", this, false); - - // Check if the new tab feature is enabled. - let enabled = gAllPages.enabled; - if (enabled) - this._init(); - - this._updateAttributes(enabled); - }, - - /** - * Listens for notifications specific to this page. - */ - observe: function Page_observe(aSubject, aTopic, aData) { - if (aTopic == "nsPref:changed") { - let enabled = gAllPages.enabled; - this._updateAttributes(enabled); - - // Initialize the whole page if we haven't done that, yet. - if (enabled) { - this._init(); - } else { - gUndoDialog.hide(); - } - } else if (aTopic == "page-thumbnail:create" && gGrid.ready) { - for (let site of gGrid.sites) { - if (site && site.url === aData) { - site.refreshThumbnail(); - } - } - } - }, - - /** - * Updates the page's grid right away for visible pages. If the page is - * currently hidden, i.e. in a background tab or in the preloader, then we - * batch multiple update requests and refresh the grid once after a short - * delay. Accepts a single parameter the specifies the reason for requesting - * a page update. The page may decide to delay or prevent a requested updated - * based on the given reason. - */ - update(reason = "") { - // Update immediately if we're visible. - if (!document.hidden) { - // Ignore updates where reason=links-changed as those signal that the - // provider's set of links changed. We don't want to update visible pages - // in that case, it is ok to wait until the user opens the next tab. - if (reason != "links-changed" && gGrid.ready) { - gGrid.refresh(); - } - - return; - } - - // Bail out if we scheduled before. - if (this._scheduleUpdateTimeout) { - return; - } - - this._scheduleUpdateTimeout = setTimeout(() => { - // Refresh if the grid is ready. - if (gGrid.ready) { - gGrid.refresh(); - } - - this._scheduleUpdateTimeout = null; - }, SCHEDULE_UPDATE_TIMEOUT_MS); - }, - - /** - * Internally initializes the page. This runs only when/if the feature - * is/gets enabled. - */ - _init: function Page_init() { - if (this._initialized) - return; - - this._initialized = true; - - // Set submit button label for when CSS background are disabled (e.g. - // high contrast mode). - document.getElementById("searchSubmit").value = - document.body.getAttribute("dir") == "ltr" ? "\u25B6" : "\u25C0"; - - if (document.hidden) { - addEventListener("visibilitychange", this); - } else { - setTimeout(() => this.onPageFirstVisible()); - } - - // Initialize and render the grid. - gGrid.init(); - - // Initialize the drop target shim. - gDropTargetShim.init(); - -#ifdef XP_MACOSX - // Workaround to prevent a delay on MacOSX due to a slow drop animation. - document.addEventListener("dragover", this, false); - document.addEventListener("drop", this, false); -#endif - }, - - /** - * Updates the 'page-disabled' attributes of the respective DOM nodes. - * @param aValue Whether the New Tab Page is enabled or not. - */ - _updateAttributes: function Page_updateAttributes(aValue) { - // Set the nodes' states. - let nodeSelector = "#newtab-grid, #searchContainer"; - for (let node of document.querySelectorAll(nodeSelector)) { - if (aValue) - node.removeAttribute("page-disabled"); - else - node.setAttribute("page-disabled", "true"); - } - - // Enables/disables the control and link elements. - let inputSelector = ".newtab-control, .newtab-link"; - for (let input of document.querySelectorAll(inputSelector)) { - if (aValue) - input.removeAttribute("tabindex"); - else - input.setAttribute("tabindex", "-1"); - } - }, - - /** - * Handles unload event - */ - _handleUnloadEvent: function Page_handleUnloadEvent() { - gAllPages.unregister(this); - }, - - /** - * Handles all page events. - */ - handleEvent: function Page_handleEvent(aEvent) { - switch (aEvent.type) { - case "load": - this.onPageVisibleAndLoaded(); - break; - case "unload": - this._handleUnloadEvent(); - break; - case "click": - let {button, target} = aEvent; - // Go up ancestors until we find a Site or not - while (target) { - if (target.hasOwnProperty("_newtabSite")) { - target._newtabSite.onClick(aEvent); - break; - } - target = target.parentNode; - } - break; - case "dragover": - if (gDrag.isValid(aEvent) && gDrag.draggedSite) - aEvent.preventDefault(); - break; - case "drop": - if (gDrag.isValid(aEvent) && gDrag.draggedSite) { - aEvent.preventDefault(); - aEvent.stopPropagation(); - } - break; - case "visibilitychange": - // Cancel any delayed updates for hidden pages now that we're visible. - if (this._scheduleUpdateTimeout) { - clearTimeout(this._scheduleUpdateTimeout); - this._scheduleUpdateTimeout = null; - - // An update was pending so force an update now. - this.update(); - } - - setTimeout(() => this.onPageFirstVisible()); - removeEventListener("visibilitychange", this); - break; - } - }, - - onPageFirstVisible: function () { - // Record another page impression. - Services.telemetry.getHistogramById("NEWTAB_PAGE_SHOWN").add(true); - - for (let site of gGrid.sites) { - if (site) { - // The site may need to modify and/or re-render itself if - // something changed after newtab was created by preloader. - // For example, the suggested tile endTime may have passed. - site.onFirstVisible(); - } - } - - // save timestamp to compute page life-span delta - this._firstVisibleTime = Date.now(); - - if (document.readyState == "complete") { - this.onPageVisibleAndLoaded(); - } else { - addEventListener("load", this); - } - }, - - onPageVisibleAndLoaded() { - }, - - toggleEnabled: function(aEvent) { - gAllPages.enabled = !gAllPages.enabled; - aEvent.stopPropagation(); - } -}; diff --git a/components/newtab/search.js b/components/newtab/search.js deleted file mode 100644 index 8bc959e..0000000 --- a/components/newtab/search.js +++ /dev/null @@ -1,134 +0,0 @@ -#ifdef 0 -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ -#endif - -const SEARCH_ENGINES = { - "DuckDuckGo": { - image: "data:image/png;base64," + - "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAACT1BMVEXvISn/////9/fvUlr3ra3/" + - "zs7/7+/va2v/5+f/xsbvMTn/tbX/3t7/vb3vOUL3WmPvQkr/zgDvKTHvSlL3hIT3paX/1tbnISn3" + - "c3v3e3v3a3P3jIz3nJz/tb33c3PvKSn3lJT39/cAc73vSkr3e4Tv7+/3Yxj3pa3/tQj3jJT3nKX3" + - "Y2P/xs73hIzvQkL/vQjvQiHn5+f3hBD/ztbvMTH/vcb/3ucIc733lJz/pQilzufe7/fvMSHOzs73" + - "//cQrUpKvVprxmP3Y2vvShiUzmvWlJRzzmMYtUrvOTnn7/davVrWra3v9//nY2PvISGUxudztd7e" + - "3t7/76XvKSHea2v/xgDnOUK93vfW5/f/1t73Uhj/52ut3q2l3rXO784pjMZrrdb/rQjera3/5+/e" + - "paWMxufO79aEazkYrUr/nAj3jBD3axj3lBD///fehIRKpd7/1hCEYzk5vVL3//8ptVLW77UxtVLn" + - "SlLW1tZCvVp7vef/1gj/3invSkL//+fWtbXvpaX/3kr/97XvnJznWmMxjM5zvefOxsbWnKXWjIzG" + - "3u/ea3Pn997O5/fnQkqExuf3Whit1u/nUlrnxs7v5+d7zmuU1pT3exDOSjFjrVL/987/pUoQe8b/" + - "75T/3jFKxnO158bWKSl7zoRSxmtajEK1e0pzxlqcUjH/1iHOMSnOvb33cxDWnJx7td6EzmP/74xz" + - "azlrcznec3Pe771jxlpzczne78YpvVqEvWPn99YxvWOtSjHee3vG787OOTE5lEK1QjHv9+drzmve" + - "tbXO772q+r8wAAAFbUlEQVR4Xo2X84PzTBDHN3Zqu2fbemzbNl7atm3btvGHvTNJ2myuyd3NL2mT" + - "zmdnvjM76RImyGQlH5dCHBeSmscNmQkyfwBrZMLEY2aRF5cMSDYPEx+LZpUlAYRQbVEpnuc1je/M" + - "SbVwYoVFAbpE0IaLmiwqiVymmE3H84YuGs2mheCEhQH5qPUrje2ONxHKVIkXR2x2MxsMkDnLvftk" + - "2fSTQNCzSAgngwCCipkXxHiU+BsnCDFE8f6AQgnwaTGhkmDLymW8jPsBeIsth8iCpha618El1wgo" + - "4FOhWyWLWY+O8pbnAwTI29S1ElncJBmF4L0AGeJSdR4dUpt5w+DL0nAgoUuGGKKCBxDCOxrykaDb" + - "+yFQjhUylLlXpAB5jGnIqV6uvvWUcAAhLmDBXIAMrkXRdHQ+cerUiWefq1hRrAgg8LikUgdkQUAx" + - "6+2Ze0WLEO/1BQzrHCFNrAPAeDSD4q/Ln6R3p68MSYzDAUiwIEutJM0bHXE/gpEhJMxaAB3T6aT8" + - "mfkm+QBiMlwKFqAHvrHu9tvTOLrEdX4hFAkJWQB42qbVyam75ruv3zvF+wBCKJ0MAAV6SAy5+raA" + - "y+lb9tYBUw9sffKRJh+CDl2SAEAPquaC76swU1c+zlxbA9if/EIY78AcCBODDKjnVzDM0+sb57zq" + - "N14gdpbg4nraBaxm3NWpIDKNgJIIDTxEAKMyVM9/VrFcpijK52PbNhmk0RQORCA8dhGhIkDA+qPV" + - "Y/U8No2NHZsUfQCdzYTECSiRSRJKgxYAnK6+tnVrPYL7q2P7GNNnT0L3SQSS61AowK4BAExWq9XJ" + - "OmDT5D4GtUab7p92W1aD6AFBOjUKcONNKMG2o9vmScmhd+v5SCTS91StDLBwmHR5q0iiM4yv3X5g" + - "sD1i24tUHc0GQOrOihdw+ZV7drx+8I1IzfpaCQ1oSIGsbqEBdxy8KkLb8dYt7m7AFBpEJI8OUIAd" + - "Hve+wX509IqYgzLqxKMi5X+r6737wgHfMrZBKGwpQMWP0PN8/8qLn15cSRosEQeI3coxGrzRVfE2" + - "BEyTAMNpmbA3k2erPOyq+CUCPGvv3OmGykYBQhiYFbynDLu2uyW826qb7bSlv/VCe2R3vQqhIYQQ" + - "nLmSGKUAT1AqXn7V6p72iUsTThsNuhKUAeKMNFaiW2nG08H90IF1m6DywVdsHgA4bPgRGgAqUgBr" + - "DwxOtPcdv9RK6yklnaGKOXBMmN7RVCtJJMiUdG2s78dv9HbY7KrI9AQBOHwjaxaA6cKhRLXCHkpF" + - "PrAJYBz1su7LtSBQIjzozgI5AJDWsQ7gTJxETTHuEh5yW8kR5+1fvQBT5PDdWgPokE6GSuK3Aaby" + - "2KwNyGFIZ8/NfexVMAGXEfe8MA5QTVdrgGe2M9evev6FMwiAYr308nVzcx/SgHwSlswyLgDLHU0K" + - "tX5UZwCwZsM1b7516J1333v/g2UAuJoCNMsmZkEDZBXujCoOIfVJxQKsvXnDshvWfrEcAV9RAoqY" + - "rfdvHjY06R3tVmtjzQYsQ8ByC/C1O0dEzqkAGqELbiZ1W/RvBr51Ad9ZgO8dQCkh4/q5xvMC6hot" + - "sBl7rP1QT+HHQz9RGoSHhkyMgqEBdNPFWSWMY+1nBPxy+MjvZ2aZxB9n/zz3FwKiOTZfotb3AhhF" + - "xSUUNmGSjX+vWvPPYacVWJOkUilUT05ymEVb0JFHj9l/AVn+35b/jsx6YzNz8mja+iAEH7rYDntY" + - "Gaz3dizW080KWaeICx77kiG7lTKG6EEoPb0Wu0lZ9OA5whFH8GxHQjOMQls5HSs5t/glHX2FYtT/" + - "mGAs/fCtFU0vQJUSQYfvIBvVyukuLhbjuood/H6WCbD/AQSFvIO3JDxgAAAAAElFTkSuQmCC" - } -}; - -// This global tracks if the page has been set up before, to prevent double inits -var gInitialized = false; -var gObserver = new MutationObserver(function (mutations) { - for (let mutation of mutations) { - if (mutation.attributeName == "searchEngineURL") { - setupSearchEngine(); - if (!gInitialized) { - gInitialized = true; - } - return; - } - } -}); - -window.addEventListener("pageshow", function () { - window.gObserver.observe(document.documentElement, { attributes: true }); -}); - -window.addEventListener("pagehide", function() { - window.gObserver.disconnect(); -}); - -function onSearchSubmit(aEvent) { - let searchTerms = document.getElementById("searchText").value; - let searchURL = document.documentElement.getAttribute("searchEngineURL"); - - if (searchURL && searchTerms.length > 0) { - const SEARCH_TOKEN = "_searchTerms_"; - let searchPostData = document.documentElement.getAttribute("searchEnginePostData"); - if (searchPostData) { - // Check if a post form already exists. If so, remove it. - const POST_FORM_NAME = "searchFormPost"; - let form = document.forms[POST_FORM_NAME]; - if (form) { - form.parentNode.removeChild(form); - } - - // Create a new post form. - form = document.body.appendChild(document.createElement("form")); - form.setAttribute("name", POST_FORM_NAME); - // Set the URL to submit the form to. - form.setAttribute("action", searchURL.replace(SEARCH_TOKEN, searchTerms)); - form.setAttribute("method", "post"); - - // Create new <input type=hidden> elements for search param. - searchPostData = searchPostData.split("&"); - for (let postVar of searchPostData) { - let [name, value] = postVar.split("="); - if (value == SEARCH_TOKEN) { - value = searchTerms; - } - let input = document.createElement("input"); - input.setAttribute("type", "hidden"); - input.setAttribute("name", name); - input.setAttribute("value", value); - form.appendChild(input); - } - // Submit the form. - form.submit(); - } else { - searchURL = searchURL.replace(SEARCH_TOKEN, encodeURIComponent(searchTerms)); - window.location.href = searchURL; - } - } - - aEvent.preventDefault(); -} - - -function setupSearchEngine() { - let searchText = document.getElementById("searchText"); - let searchEngineName = document.documentElement.getAttribute("searchEngineName"); - let searchEngineInfo = SEARCH_ENGINES[searchEngineName]; - let logoElt = document.getElementById("searchEngineLogo"); - - // Add search engine logo. - if (searchEngineInfo && searchEngineInfo.image) { - logoElt.parentNode.hidden = false; - logoElt.src = searchEngineInfo.image; - logoElt.alt = searchEngineName; - searchText.placeholder = ""; - } else { - logoElt.parentNode.hidden = true; - searchText.placeholder = searchEngineName; - } -} diff --git a/components/newtab/sites.js b/components/newtab/sites.js deleted file mode 100644 index cb56752..0000000 --- a/components/newtab/sites.js +++ /dev/null @@ -1,353 +0,0 @@ -#ifdef 0 -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ -#endif - -const THUMBNAIL_PLACEHOLDER_ENABLED = - Services.prefs.getBoolPref("browser.newtabpage.thumbnailPlaceholder"); - -/** - * This class represents a site that is contained in a cell and can be pinned, - * moved around or deleted. - */ -function Site(aNode, aLink) { - this._node = aNode; - this._node._newtabSite = this; - - this._link = aLink; - - this._render(); - this._addEventHandlers(); -} - -Site.prototype = { - /** - * The site's DOM node. - */ - get node() { return this._node; }, - - /** - * The site's link. - */ - get link() { return this._link; }, - - /** - * The url of the site's link. - */ - get url() { return this.link.url; }, - - /** - * The title of the site's link. - */ - get title() { return this.link.title || this.link.url; }, - - /** - * The site's parent cell. - */ - get cell() { - let parentNode = this.node.parentNode; - return parentNode && parentNode._newtabCell; - }, - - /** - * Pins the site on its current or a given index. - * @param aIndex The pinned index (optional). - * @return true if link changed type after pin - */ - pin: function Site_pin(aIndex) { - if (typeof aIndex == "undefined") - aIndex = this.cell.index; - - this._updateAttributes(true); - let changed = gPinnedLinks.pin(this._link, aIndex); - if (changed) { - // render site again - this._render(); - } - return changed; - }, - - /** - * Unpins the site and calls the given callback when done. - */ - unpin: function Site_unpin() { - if (this.isPinned()) { - this._updateAttributes(false); - gPinnedLinks.unpin(this._link); - gUpdater.updateGrid(); - } - }, - - /** - * Checks whether this site is pinned. - * @return Whether this site is pinned. - */ - isPinned: function Site_isPinned() { - return gPinnedLinks.isPinned(this._link); - }, - - /** - * Blocks the site (removes it from the grid) and calls the given callback - * when done. - */ - block: function Site_block() { - if (!gBlockedLinks.isBlocked(this._link)) { - gUndoDialog.show(this); - gBlockedLinks.block(this._link); - gUpdater.updateGrid(); - } - }, - - /** - * Gets the DOM node specified by the given query selector. - * @param aSelector The query selector. - * @return The DOM node we found. - */ - _querySelector: function Site_querySelector(aSelector) { - return this.node.querySelector(aSelector); - }, - - /** - * Updates attributes for all nodes which status depends on this site being - * pinned or unpinned. - * @param aPinned Whether this site is now pinned or unpinned. - */ - _updateAttributes: function (aPinned) { - let control = this._querySelector(".newtab-control-pin"); - - if (aPinned) { - this.node.setAttribute("pinned", true); - control.setAttribute("title", newTabString("unpin")); - } else { - this.node.removeAttribute("pinned"); - control.setAttribute("title", newTabString("pin")); - } - }, - - _newTabString: function(str, substrArr) { - let regExp = /%[0-9]\$S/g; - let matches; - while ((matches = regExp.exec(str))) { - let match = matches[0]; - let index = match.charAt(1); // Get the digit in the regExp. - str = str.replace(match, substrArr[index - 1]); - } - return str; - }, - - /** - * Checks for and modifies link at campaign end time - */ - _checkLinkEndTime: function Site_checkLinkEndTime() { - if (this.link.endTime && this.link.endTime < Date.now()) { - let oldUrl = this.url; - // chop off the path part from url - this.link.url = Services.io.newURI(this.url, null, null).resolve("/"); - // clear supplied images - this triggers thumbnail download for new url - delete this.link.imageURI; - // remove endTime to avoid further time checks - delete this.link.endTime; - gPinnedLinks.replace(oldUrl, this.link); - } - }, - - /** - * Renders the site's data (fills the HTML fragment). - */ - _render: function Site_render() { - // first check for end time, as it may modify the link - this._checkLinkEndTime(); - // setup display variables - let url = this.url; - let title = this.link.type == "history" ? this.link.baseDomain : - this.title; - let tooltip = (this.title == url ? this.title : this.title + "\n" + url); - - let link = this._querySelector(".newtab-link"); - link.setAttribute("title", tooltip); - link.setAttribute("href", url); - this.node.setAttribute("type", this.link.type); - - let titleNode = this._querySelector(".newtab-title"); - titleNode.textContent = title; - if (this.link.titleBgColor) { - titleNode.style.backgroundColor = this.link.titleBgColor; - } - - if (this.isPinned()) - this._updateAttributes(true); - // Capture the page if the thumbnail is missing, which will cause page.js - // to be notified and call our refreshThumbnail() method. - this.captureIfMissing(); - // but still display whatever thumbnail might be available now. - this.refreshThumbnail(); - }, - - /** - * Called when the site's tab becomes visible for the first time. - * Since the newtab may be preloaded long before it's displayed, - * check for changed conditions and re-render if needed - */ - onFirstVisible: function Site_onFirstVisible() { - if (this.link.endTime && this.link.endTime < Date.now()) { - // site needs to change landing url and background image - this._render(); - } - else { - this.captureIfMissing(); - } - }, - - /** - * Captures the site's thumbnail in the background, but only if there's no - * existing thumbnail and the page allows background captures. - */ - captureIfMissing: function Site_captureIfMissing() { - if (!document.hidden && !this.link.imageURI) { - BackgroundPageThumbs.captureIfMissing(this.url); - } - }, - - /** - * Refreshes the thumbnail for the site. - */ - refreshThumbnail: function Site_refreshThumbnail() { - let link = this.link; - - let thumbnail = this._querySelector(".newtab-thumbnail.thumbnail"); - if (link.bgColor) { - thumbnail.style.backgroundColor = link.bgColor; - } - let uri = link.imageURI || PageThumbs.getThumbnailURL(this.url); - thumbnail.style.backgroundImage = 'url("' + uri + '")'; - - if (THUMBNAIL_PLACEHOLDER_ENABLED && - link.type == "history" && - link.baseDomain) { - let placeholder = this._querySelector(".newtab-thumbnail.placeholder"); - let charCodeSum = 0; - for (let c of link.baseDomain) { - charCodeSum += c.charCodeAt(0); - } - const COLORS = 16; - let hue = Math.round((charCodeSum % COLORS) / COLORS * 360); - placeholder.style.backgroundColor = "hsl(" + hue + ",80%,40%)"; - placeholder.textContent = link.baseDomain.substr(0,1).toUpperCase(); - } - }, - - _ignoreHoverEvents: function(element) { - element.addEventListener("mouseover", () => { - this.cell.node.setAttribute("ignorehover", "true"); - }); - element.addEventListener("mouseout", () => { - this.cell.node.removeAttribute("ignorehover"); - }); - }, - - /** - * Adds event handlers for the site and its buttons. - */ - _addEventHandlers: function Site_addEventHandlers() { - // Register drag-and-drop event handlers. - this._node.addEventListener("dragstart", this, false); - this._node.addEventListener("dragend", this, false); - this._node.addEventListener("mouseover", this, false); - }, - - /** - * Speculatively opens a connection to the current site. - */ - _speculativeConnect: function Site_speculativeConnect() { - let sc = Services.io.QueryInterface(Ci.nsISpeculativeConnect); - let uri = Services.io.newURI(this.url, null, null); - try { - // This can throw for certain internal URLs, when they wind up in - // about:newtab. Be sure not to propagate the error. - sc.speculativeConnect(uri, null); - } catch (e) {} - }, - - /** - * Record interaction with site using telemetry. - */ - _recordSiteClicked: function Site_recordSiteClicked(aIndex) { - if (Services.prefs.prefHasUserValue("browser.newtabpage.rows") || - Services.prefs.prefHasUserValue("browser.newtabpage.columns") || - aIndex > 8) { - // We only want to get indices for the default configuration, everything - // else goes in the same bucket. - aIndex = 9; - } - Services.telemetry.getHistogramById("NEWTAB_PAGE_SITE_CLICKED") - .add(aIndex); - }, - - _toggleLegalText: function(buttonClass, explanationTextClass) { - let button = this._querySelector(buttonClass); - if (button.hasAttribute("active")) { - let explain = this._querySelector(explanationTextClass); - explain.parentNode.removeChild(explain); - - button.removeAttribute("active"); - } - }, - - /** - * Handles site click events. - */ - onClick: function Site_onClick(aEvent) { - let action; - let pinned = this.isPinned(); - let tileIndex = this.cell.index; - let {button, target} = aEvent; - - // Handle tile/thumbnail link click - if (target.classList.contains("newtab-link") || - target.parentElement.classList.contains("newtab-link")) { - // Record for primary and middle clicks - if (button == 0 || button == 1) { - this._recordSiteClicked(tileIndex); - action = "click"; - } - } - // Only handle primary clicks for the remaining targets - else if (button == 0) { - aEvent.preventDefault(); - if (target.classList.contains("newtab-control-block")) { - this.block(); - action = "block"; - } - else if (pinned && target.classList.contains("newtab-control-pin")) { - this.unpin(); - action = "unpin"; - } - else if (!pinned && target.classList.contains("newtab-control-pin")) { - if (this.pin()) { - // link has changed - update rest of the pages - gAllPages.update(gPage); - } - action = "pin"; - } - } - }, - - /** - * Handles all site events. - */ - handleEvent: function Site_handleEvent(aEvent) { - switch (aEvent.type) { - case "mouseover": - this._node.removeEventListener("mouseover", this, false); - this._speculativeConnect(); - break; - case "dragstart": - gDrag.start(this, aEvent); - break; - case "dragend": - gDrag.end(this, aEvent); - break; - } - } -}; diff --git a/components/newtab/transformations.js b/components/newtab/transformations.js deleted file mode 100644 index f7db0ad..0000000 --- a/components/newtab/transformations.js +++ /dev/null @@ -1,270 +0,0 @@ -#ifdef 0 -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ -#endif - -/** - * This singleton allows to transform the grid by repositioning a site's node - * in the DOM and by showing or hiding the node. It additionally provides - * convenience methods to work with a site's DOM node. - */ -var gTransformation = { - /** - * Returns the width of the left and top border of a cell. We need to take it - * into account when measuring and comparing site and cell positions. - */ - get _cellBorderWidths() { - let cstyle = window.getComputedStyle(gGrid.cells[0].node, null); - let widths = { - left: parseInt(cstyle.getPropertyValue("border-left-width")), - top: parseInt(cstyle.getPropertyValue("border-top-width")) - }; - - // Cache this value, overwrite the getter. - Object.defineProperty(this, "_cellBorderWidths", - {value: widths, enumerable: true}); - - return widths; - }, - - /** - * Gets a DOM node's position. - * @param aNode The DOM node. - * @return A Rect instance with the position. - */ - getNodePosition: function Transformation_getNodePosition(aNode) { - let {left, top, width, height} = aNode.getBoundingClientRect(); - return new Rect(left + scrollX, top + scrollY, width, height); - }, - - /** - * Fades a given node from zero to full opacity. - * @param aNode The node to fade. - * @param aCallback The callback to call when finished. - */ - fadeNodeIn: function Transformation_fadeNodeIn(aNode, aCallback) { - this._setNodeOpacity(aNode, 1, function () { - // Clear the style property. - aNode.style.opacity = ""; - - if (aCallback) - aCallback(); - }); - }, - - /** - * Fades a given node from full to zero opacity. - * @param aNode The node to fade. - * @param aCallback The callback to call when finished. - */ - fadeNodeOut: function Transformation_fadeNodeOut(aNode, aCallback) { - this._setNodeOpacity(aNode, 0, aCallback); - }, - - /** - * Fades a given site from zero to full opacity. - * @param aSite The site to fade. - * @param aCallback The callback to call when finished. - */ - showSite: function Transformation_showSite(aSite, aCallback) { - this.fadeNodeIn(aSite.node, aCallback); - }, - - /** - * Fades a given site from full to zero opacity. - * @param aSite The site to fade. - * @param aCallback The callback to call when finished. - */ - hideSite: function Transformation_hideSite(aSite, aCallback) { - this.fadeNodeOut(aSite.node, aCallback); - }, - - /** - * Allows to set a site's position. - * @param aSite The site to re-position. - * @param aPosition The desired position for the given site. - */ - setSitePosition: function Transformation_setSitePosition(aSite, aPosition) { - let style = aSite.node.style; - let {top, left} = aPosition; - - style.top = top + "px"; - style.left = left + "px"; - }, - - /** - * Freezes a site in its current position by positioning it absolute. - * @param aSite The site to freeze. - */ - freezeSitePosition: function Transformation_freezeSitePosition(aSite) { - if (this._isFrozen(aSite)) - return; - - let style = aSite.node.style; - let comp = getComputedStyle(aSite.node, null); - style.width = comp.getPropertyValue("width"); - style.height = comp.getPropertyValue("height"); - - aSite.node.setAttribute("frozen", "true"); - this.setSitePosition(aSite, this.getNodePosition(aSite.node)); - }, - - /** - * Unfreezes a site by removing its absolute positioning. - * @param aSite The site to unfreeze. - */ - unfreezeSitePosition: function Transformation_unfreezeSitePosition(aSite) { - if (!this._isFrozen(aSite)) - return; - - let style = aSite.node.style; - style.left = style.top = style.width = style.height = ""; - aSite.node.removeAttribute("frozen"); - }, - - /** - * Slides the given site to the target node's position. - * @param aSite The site to move. - * @param aTarget The slide target. - * @param aOptions Set of options (see below). - * unfreeze - unfreeze the site after sliding - * callback - the callback to call when finished - */ - slideSiteTo: function Transformation_slideSiteTo(aSite, aTarget, aOptions) { - let currentPosition = this.getNodePosition(aSite.node); - let targetPosition = this.getNodePosition(aTarget.node) - let callback = aOptions && aOptions.callback; - - let self = this; - - function finish() { - if (aOptions && aOptions.unfreeze) - self.unfreezeSitePosition(aSite); - - if (callback) - callback(); - } - - // We need to take the width of a cell's border into account. - targetPosition.left += this._cellBorderWidths.left; - targetPosition.top += this._cellBorderWidths.top; - - // Nothing to do here if the positions already match. - if (currentPosition.left == targetPosition.left && - currentPosition.top == targetPosition.top) { - finish(); - } else { - this.setSitePosition(aSite, targetPosition); - this._whenTransitionEnded(aSite.node, ["left", "top"], finish); - } - }, - - /** - * Rearranges a given array of sites and moves them to their new positions or - * fades in/out new/removed sites. - * @param aSites An array of sites to rearrange. - * @param aOptions Set of options (see below). - * unfreeze - unfreeze the site after rearranging - * callback - the callback to call when finished - */ - rearrangeSites: function Transformation_rearrangeSites(aSites, aOptions) { - let batch = []; - let cells = gGrid.cells; - let callback = aOptions && aOptions.callback; - let unfreeze = aOptions && aOptions.unfreeze; - - aSites.forEach(function (aSite, aIndex) { - // Do not re-arrange empty cells or the dragged site. - if (!aSite || aSite == gDrag.draggedSite) - return; - - batch.push(new Promise(resolve => { - if (!cells[aIndex]) { - // The site disappeared from the grid, hide it. - this.hideSite(aSite, resolve); - } else if (this._getNodeOpacity(aSite.node) != 1) { - // The site disappeared before but is now back, show it. - this.showSite(aSite, resolve); - } else { - // The site's position has changed, move it around. - this._moveSite(aSite, aIndex, {unfreeze: unfreeze, callback: resolve}); - } - })); - }, this); - - if (callback) { - Promise.all(batch).then(callback); - } - }, - - /** - * Listens for the 'transitionend' event on a given node and calls the given - * callback. - * @param aNode The node that is transitioned. - * @param aProperties The properties we'll wait to be transitioned. - * @param aCallback The callback to call when finished. - */ - _whenTransitionEnded: - function Transformation_whenTransitionEnded(aNode, aProperties, aCallback) { - - let props = new Set(aProperties); - aNode.addEventListener("transitionend", function onEnd(e) { - if (props.has(e.propertyName)) { - aNode.removeEventListener("transitionend", onEnd); - aCallback(); - } - }); - }, - - /** - * Gets a given node's opacity value. - * @param aNode The node to get the opacity value from. - * @return The node's opacity value. - */ - _getNodeOpacity: function Transformation_getNodeOpacity(aNode) { - let cstyle = window.getComputedStyle(aNode, null); - return cstyle.getPropertyValue("opacity"); - }, - - /** - * Sets a given node's opacity. - * @param aNode The node to set the opacity value for. - * @param aOpacity The opacity value to set. - * @param aCallback The callback to call when finished. - */ - _setNodeOpacity: - function Transformation_setNodeOpacity(aNode, aOpacity, aCallback) { - - if (this._getNodeOpacity(aNode) == aOpacity) { - if (aCallback) - aCallback(); - } else { - if (aCallback) { - this._whenTransitionEnded(aNode, ["opacity"], aCallback); - } - - aNode.style.opacity = aOpacity; - } - }, - - /** - * Moves a site to the cell with the given index. - * @param aSite The site to move. - * @param aIndex The target cell's index. - * @param aOptions Options that are directly passed to slideSiteTo(). - */ - _moveSite: function Transformation_moveSite(aSite, aIndex, aOptions) { - this.freezeSitePosition(aSite); - this.slideSiteTo(aSite, gGrid.cells[aIndex], aOptions); - }, - - /** - * Checks whether a site is currently frozen. - * @param aSite The site to check. - * @return Whether the given site is frozen. - */ - _isFrozen: function Transformation_isFrozen(aSite) { - return aSite.node.hasAttribute("frozen"); - } -}; diff --git a/components/newtab/undo.js b/components/newtab/undo.js deleted file mode 100644 index b856914..0000000 --- a/components/newtab/undo.js +++ /dev/null @@ -1,116 +0,0 @@ -#ifdef 0 -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ -#endif - -/** - * Dialog allowing to undo the removal of single site or to completely restore - * the grid's original state. - */ -var gUndoDialog = { - /** - * The undo dialog's timeout in miliseconds. - */ - HIDE_TIMEOUT_MS: 15000, - - /** - * Contains undo information. - */ - _undoData: null, - - /** - * Initializes the undo dialog. - */ - init: function UndoDialog_init() { - this._undoContainer = document.getElementById("newtab-undo-container"); - this._undoContainer.addEventListener("click", this, false); - this._undoButton = document.getElementById("newtab-undo-button"); - this._undoCloseButton = document.getElementById("newtab-undo-close-button"); - this._undoRestoreButton = document.getElementById("newtab-undo-restore-button"); - }, - - /** - * Shows the undo dialog. - * @param aSite The site that just got removed. - */ - show: function UndoDialog_show(aSite) { - if (this._undoData) - clearTimeout(this._undoData.timeout); - - this._undoData = { - index: aSite.cell.index, - wasPinned: aSite.isPinned(), - blockedLink: aSite.link, - timeout: setTimeout(this.hide.bind(this), this.HIDE_TIMEOUT_MS) - }; - - this._undoContainer.removeAttribute("undo-disabled"); - this._undoButton.removeAttribute("tabindex"); - this._undoCloseButton.removeAttribute("tabindex"); - this._undoRestoreButton.removeAttribute("tabindex"); - }, - - /** - * Hides the undo dialog. - */ - hide: function UndoDialog_hide() { - if (!this._undoData) - return; - - clearTimeout(this._undoData.timeout); - this._undoData = null; - this._undoContainer.setAttribute("undo-disabled", "true"); - this._undoButton.setAttribute("tabindex", "-1"); - this._undoCloseButton.setAttribute("tabindex", "-1"); - this._undoRestoreButton.setAttribute("tabindex", "-1"); - }, - - /** - * The undo dialog event handler. - * @param aEvent The event to handle. - */ - handleEvent: function UndoDialog_handleEvent(aEvent) { - switch (aEvent.target.id) { - case "newtab-undo-button": - this._undo(); - break; - case "newtab-undo-restore-button": - this._undoAll(); - break; - case "newtab-undo-close-button": - this.hide(); - break; - } - }, - - /** - * Undo the last blocked site. - */ - _undo: function UndoDialog_undo() { - if (!this._undoData) - return; - - let {index, wasPinned, blockedLink} = this._undoData; - gBlockedLinks.unblock(blockedLink); - - if (wasPinned) { - gPinnedLinks.pin(blockedLink, index); - } - - gUpdater.updateGrid(); - this.hide(); - }, - - /** - * Undo all blocked sites. - */ - _undoAll: function UndoDialog_undoAll() { - NewTabUtils.undoAll(function() { - gUpdater.updateGrid(); - this.hide(); - }.bind(this)); - } -}; - -gUndoDialog.init(); diff --git a/components/newtab/updater.js b/components/newtab/updater.js deleted file mode 100644 index 2bab74d..0000000 --- a/components/newtab/updater.js +++ /dev/null @@ -1,177 +0,0 @@ -#ifdef 0 -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ -#endif - -/** - * This singleton provides functionality to update the current grid to a new - * set of pinned and blocked sites. It adds, moves and removes sites. - */ -var gUpdater = { - /** - * Updates the current grid according to its pinned and blocked sites. - * This removes old, moves existing and creates new sites to fill gaps. - * @param aCallback The callback to call when finished. - */ - updateGrid: function Updater_updateGrid(aCallback) { - let links = gLinks.getLinks().slice(0, gGrid.cells.length); - - // Find all sites that remain in the grid. - let sites = this._findRemainingSites(links); - - // Remove sites that are no longer in the grid. - this._removeLegacySites(sites, () => { - // Freeze all site positions so that we can move their DOM nodes around - // without any visual impact. - this._freezeSitePositions(sites); - - // Move the sites' DOM nodes to their new position in the DOM. This will - // have no visual effect as all the sites have been frozen and will - // remain in their current position. - this._moveSiteNodes(sites); - - // Now it's time to animate the sites actually moving to their new - // positions. - this._rearrangeSites(sites, () => { - // Try to fill empty cells and finish. - this._fillEmptyCells(links, aCallback); - - // Update other pages that might be open to keep them synced. - gAllPages.update(gPage); - }); - }); - }, - - /** - * Takes an array of links and tries to correlate them to sites contained in - * the current grid. If no corresponding site can be found (i.e. the link is - * new and a site will be created) then just set it to null. - * @param aLinks The array of links to find sites for. - * @return Array of sites mapped to the given links (can contain null values). - */ - _findRemainingSites: function Updater_findRemainingSites(aLinks) { - let map = {}; - - // Create a map to easily retrieve the site for a given URL. - gGrid.sites.forEach(function (aSite) { - if (aSite) - map[aSite.url] = aSite; - }); - - // Map each link to its corresponding site, if any. - return aLinks.map(function (aLink) { - return aLink && (aLink.url in map) && map[aLink.url]; - }); - }, - - /** - * Freezes the given sites' positions. - * @param aSites The array of sites to freeze. - */ - _freezeSitePositions: function Updater_freezeSitePositions(aSites) { - aSites.forEach(function (aSite) { - if (aSite) - gTransformation.freezeSitePosition(aSite); - }); - }, - - /** - * Moves the given sites' DOM nodes to their new positions. - * @param aSites The array of sites to move. - */ - _moveSiteNodes: function Updater_moveSiteNodes(aSites) { - let cells = gGrid.cells; - - // Truncate the given array of sites to not have more sites than cells. - // This can happen when the user drags a bookmark (or any other new kind - // of link) onto the grid. - let sites = aSites.slice(0, cells.length); - - sites.forEach(function (aSite, aIndex) { - let cell = cells[aIndex]; - let cellSite = cell.site; - - // The site's position didn't change. - if (!aSite || cellSite != aSite) { - let cellNode = cell.node; - - // Empty the cell if necessary. - if (cellSite) - cellNode.removeChild(cellSite.node); - - // Put the new site in place, if any. - if (aSite) - cellNode.appendChild(aSite.node); - } - }, this); - }, - - /** - * Rearranges the given sites and slides them to their new positions. - * @param aSites The array of sites to re-arrange. - * @param aCallback The callback to call when finished. - */ - _rearrangeSites: function Updater_rearrangeSites(aSites, aCallback) { - let options = {callback: aCallback, unfreeze: true}; - gTransformation.rearrangeSites(aSites, options); - }, - - /** - * Removes all sites from the grid that are not in the given links array or - * exceed the grid. - * @param aSites The array of sites remaining in the grid. - * @param aCallback The callback to call when finished. - */ - _removeLegacySites: function Updater_removeLegacySites(aSites, aCallback) { - let batch = []; - - // Delete sites that were removed from the grid. - gGrid.sites.forEach(function (aSite) { - // The site must be valid and not in the current grid. - if (!aSite || aSites.indexOf(aSite) != -1) - return; - - batch.push(new Promise(resolve => { - // Fade out the to-be-removed site. - gTransformation.hideSite(aSite, function () { - let node = aSite.node; - - // Remove the site from the DOM. - node.parentNode.removeChild(node); - resolve(); - }); - })); - }); - - Promise.all(batch).then(aCallback); - }, - - /** - * Tries to fill empty cells with new links if available. - * @param aLinks The array of links. - * @param aCallback The callback to call when finished. - */ - _fillEmptyCells: function Updater_fillEmptyCells(aLinks, aCallback) { - let {cells, sites} = gGrid; - - // Find empty cells and fill them. - Promise.all(sites.map((aSite, aIndex) => { - if (aSite || !aLinks[aIndex]) - return null; - - return new Promise(resolve => { - // Create the new site and fade it in. - let site = gGrid.createSite(aLinks[aIndex], cells[aIndex]); - - // Set the site's initial opacity to zero. - site.node.style.opacity = 0; - - // Flush all style changes for the dynamically inserted site to make - // the fade-in transition work. - window.getComputedStyle(site.node).opacity; - gTransformation.showSite(site, resolve); - }); - })).then(aCallback).catch(console.exception); - } -}; diff --git a/components/nsAboutRedirector.js b/components/nsAboutRedirector.js deleted file mode 100644 index 4d99a78..0000000 --- a/components/nsAboutRedirector.js +++ /dev/null @@ -1,114 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var Ci = Components.interfaces; -var Cr = Components.results; -var Cu = Components.utils; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -// See: netwerk/protocol/about/nsIAboutModule.idl -const URI_SAFE_FOR_UNTRUSTED_CONTENT = Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT; -const ALLOW_SCRIPT = Ci.nsIAboutModule.ALLOW_SCRIPT; -const HIDE_FROM_ABOUTABOUT = Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT; -const MAKE_LINKABLE = Ci.nsIAboutModule.MAKE_LINKABLE; - -function AboutRedirector() {} -AboutRedirector.prototype = { - classDescription: "Browser about: Redirector", - classID: Components.ID("{8cc51368-6aa0-43e8-b762-bde9b9fd828c}"), - QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]), - - // Each entry in the map has the key as the part after the "about:" and the - // value as a record with url and flags entries. Note that each addition here - // should be coupled with a corresponding addition in BrowserComponents.manifest. - _redirMap: { - "certerror": { - url: "chrome://browser/content/certerror/aboutCertError.xhtml", - flags: (URI_SAFE_FOR_UNTRUSTED_CONTENT | ALLOW_SCRIPT | HIDE_FROM_ABOUTABOUT) - }, - "downloads": { - url: "chrome://browser/content/downloads/contentAreaDownloadsView.xul", - flags: ALLOW_SCRIPT - }, - "feeds": { - url: "chrome://browser/content/feeds/subscribe.xhtml", - flags: (URI_SAFE_FOR_UNTRUSTED_CONTENT | ALLOW_SCRIPT | HIDE_FROM_ABOUTABOUT) - }, - "home": { - url: "chrome://browser/content/abouthome/aboutHome.xhtml", - flags: (URI_SAFE_FOR_UNTRUSTED_CONTENT | MAKE_LINKABLE | ALLOW_SCRIPT) - }, - "newtab": { - url: "chrome://browser/content/newtab/newTab.xhtml", - flags: ALLOW_SCRIPT - }, - "palemoon": { - url: "chrome://browser/content/palemoon.xhtml", - flags: (URI_SAFE_FOR_UNTRUSTED_CONTENT | HIDE_FROM_ABOUTABOUT) - }, - "permissions": { - url: "chrome://browser/content/permissions/aboutPermissions.xul", - flags: ALLOW_SCRIPT - }, - "privatebrowsing": { - url: "chrome://browser/content/aboutPrivateBrowsing.xhtml", - flags: ALLOW_SCRIPT - }, - "rights": { - url: "chrome://global/content/aboutRights.xhtml", - flags: (URI_SAFE_FOR_UNTRUSTED_CONTENT | MAKE_LINKABLE | ALLOW_SCRIPT) - }, - "sessionrestore": { - url: "chrome://browser/content/aboutSessionRestore.xhtml", - flags: ALLOW_SCRIPT - }, -#ifdef MOZ_SERVICES_SYNC - "sync-progress": { - url: "chrome://browser/content/sync/progress.xhtml", - flags: ALLOW_SCRIPT - }, - "sync-tabs": { - url: "chrome://browser/content/sync/aboutSyncTabs.xul", - flags: ALLOW_SCRIPT - }, -#endif - }, - - /** - * Gets the module name from the given URI. - */ - _getModuleName: function AboutRedirector__getModuleName(aURI) { - // Strip out the first ? or #, and anything following it - let name = (/[^?#]+/.exec(aURI.path))[0]; - return name.toLowerCase(); - }, - - getURIFlags: function(aURI) { - let name = this._getModuleName(aURI); - if (!(name in this._redirMap)) - throw Cr.NS_ERROR_ILLEGAL_VALUE; - return this._redirMap[name].flags; - }, - - newChannel: function(aURI, aLoadInfo) { - let name = this._getModuleName(aURI); - if (!(name in this._redirMap)) - throw Cr.NS_ERROR_ILLEGAL_VALUE; - - let newURI = Services.io.newURI(this._redirMap[name].url, null, null); - let channel = Services.io.newChannelFromURIWithLoadInfo(newURI, aLoadInfo); - channel.originalURI = aURI; - - if (this._redirMap[name].flags & Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT) { - let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(aURI); - channel.owner = principal; - } - - return channel; - } -}; - -var NSGetFactory = XPCOMUtils.generateNSGetFactory([AboutRedirector]); diff --git a/components/nsBrowserContentHandler.js b/components/nsBrowserContentHandler.js deleted file mode 100644 index e7f1414..0000000 --- a/components/nsBrowserContentHandler.js +++ /dev/null @@ -1,803 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); -Components.utils.import("resource://gre/modules/Services.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", - "resource://gre/modules/PrivateBrowsingUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow", - "resource:///modules/RecentWindow.jsm"); - -const nsISupports = Components.interfaces.nsISupports; - -const nsIBrowserDOMWindow = Components.interfaces.nsIBrowserDOMWindow; -const nsIBrowserHandler = Components.interfaces.nsIBrowserHandler; -const nsIBrowserHistory = Components.interfaces.nsIBrowserHistory; -const nsIChannel = Components.interfaces.nsIChannel; -const nsICommandLine = Components.interfaces.nsICommandLine; -const nsICommandLineHandler = Components.interfaces.nsICommandLineHandler; -const nsIContentHandler = Components.interfaces.nsIContentHandler; -const nsIDocShellTreeItem = Components.interfaces.nsIDocShellTreeItem; -const nsIDOMChromeWindow = Components.interfaces.nsIDOMChromeWindow; -const nsIDOMWindow = Components.interfaces.nsIDOMWindow; -const nsIFileURL = Components.interfaces.nsIFileURL; -const nsIInterfaceRequestor = Components.interfaces.nsIInterfaceRequestor; -const nsINetUtil = Components.interfaces.nsINetUtil; -const nsIPrefBranch = Components.interfaces.nsIPrefBranch; -const nsIPrefLocalizedString = Components.interfaces.nsIPrefLocalizedString; -const nsISupportsString = Components.interfaces.nsISupportsString; -const nsIURIFixup = Components.interfaces.nsIURIFixup; -const nsIWebNavigation = Components.interfaces.nsIWebNavigation; -const nsIWindowMediator = Components.interfaces.nsIWindowMediator; -const nsIWindowWatcher = Components.interfaces.nsIWindowWatcher; -const nsIWebNavigationInfo = Components.interfaces.nsIWebNavigationInfo; -const nsIBrowserSearchService = Components.interfaces.nsIBrowserSearchService; -const nsICommandLineValidator = Components.interfaces.nsICommandLineValidator; - -const NS_BINDING_ABORTED = Components.results.NS_BINDING_ABORTED; -const NS_ERROR_WONT_HANDLE_CONTENT = 0x805d0001; -const NS_ERROR_ABORT = Components.results.NS_ERROR_ABORT; - -const URI_INHERITS_SECURITY_CONTEXT = Components.interfaces.nsIHttpProtocolHandler - .URI_INHERITS_SECURITY_CONTEXT; - -function shouldLoadURI(aURI) { - if (aURI && !aURI.schemeIs("chrome")) - return true; - - dump("*** Preventing external load of chrome: URI into browser window\n"); - dump(" Use -chrome <uri> instead\n"); - return false; -} - -function resolveURIInternal(aCmdLine, aArgument) { - var uri = aCmdLine.resolveURI(aArgument); - var urifixup = Components.classes["@mozilla.org/docshell/urifixup;1"] - .getService(nsIURIFixup); - - if (!(uri instanceof nsIFileURL)) { - return urifixup.createFixupURI(aArgument, - urifixup.FIXUP_FLAG_FIX_SCHEME_TYPOS); - } - - try { - if (uri.file.exists()) - return uri; - } - catch (e) { - Components.utils.reportError(e); - } - - // We have interpreted the argument as a relative file URI, but the file - // doesn't exist. Try URI fixup heuristics: see bug 290782. - - try { - uri = urifixup.createFixupURI(aArgument, 0); - } - catch (e) { - Components.utils.reportError(e); - } - - return uri; -} - -var gFirstWindow = false; - -const OVERRIDE_NONE = 0; -const OVERRIDE_NEW_PROFILE = 1; -const OVERRIDE_NEW_MSTONE = 2; -const OVERRIDE_NEW_BUILD_ID = 3; -/** - * Determines whether a home page override is needed. - * Returns: - * OVERRIDE_NEW_PROFILE if this is the first run with a new profile. - * OVERRIDE_NEW_MSTONE if this is the first run with a build with a different - * Goanna milestone (i.e. right after an upgrade). - * OVERRIDE_NEW_BUILD_ID if this is the first run with a new build ID of the - * same Goanna milestone (i.e. after a nightly upgrade). - * OVERRIDE_NONE otherwise. - */ -function needHomepageOverride(prefb) { - var savedmstone = prefb.getCharPref("browser.startup.homepage_override.mstone", ""); - - if (savedmstone == "ignore") - return OVERRIDE_NONE; - - var mstone = Services.appinfo.platformVersion; - - var savedBuildID = prefb.getCharPref("browser.startup.homepage_override.buildID", ""); - - var buildID = Services.appinfo.platformBuildID; - - if (mstone != savedmstone) { - // Bug 462254. Previous releases had a default pref to suppress the EULA - // agreement if the platform's installer had already shown one. Now with - // about:rights we've removed the EULA stuff and default pref, but we need - // a way to make existing profiles retain the default that we removed. - if (savedmstone) - prefb.setBoolPref("browser.rights.3.shown", true); - - prefb.setCharPref("browser.startup.homepage_override.mstone", mstone); - prefb.setCharPref("browser.startup.homepage_override.buildID", buildID); - return (savedmstone ? OVERRIDE_NEW_MSTONE : OVERRIDE_NEW_PROFILE); - } - - if (buildID != savedBuildID) { - prefb.setCharPref("browser.startup.homepage_override.buildID", buildID); - return OVERRIDE_NEW_BUILD_ID; - } - - return OVERRIDE_NONE; -} - -/** - * Gets the override page for the first run after the application has been - * updated. - * @param defaultOverridePage - * The default override page. - * @return The override page. - */ -function getPostUpdateOverridePage(defaultOverridePage) { - var um = Components.classes["@mozilla.org/updates/update-manager;1"] - .getService(Components.interfaces.nsIUpdateManager); - try { - // If the updates.xml file is deleted then getUpdateAt will throw. - var update = um.getUpdateAt(0) - .QueryInterface(Components.interfaces.nsIPropertyBag); - } catch (e) { - // This should never happen. - Components.utils.reportError("Unable to find update: " + e); - return defaultOverridePage; - } - - let actions = update.getProperty("actions"); - // When the update doesn't specify actions fallback to the original behavior - // of displaying the default override page. - if (!actions) - return defaultOverridePage; - - // The existence of silent or the non-existence of showURL in the actions both - // mean that an override page should not be displayed. - if (actions.indexOf("silent") != -1 || actions.indexOf("showURL") == -1) - return ""; - - return update.getProperty("openURL") || defaultOverridePage; -} - -// Flag used to indicate that the arguments to openWindow can be passed directly. -const NO_EXTERNAL_URIS = 1; - -function openWindow(parent, url, target, features, args, noExternalArgs) { - var wwatch = Components.classes["@mozilla.org/embedcomp/window-watcher;1"] - .getService(nsIWindowWatcher); - - if (noExternalArgs == NO_EXTERNAL_URIS) { - // Just pass in the defaultArgs directly - var argstring; - if (args) { - argstring = Components.classes["@mozilla.org/supports-string;1"] - .createInstance(nsISupportsString); - argstring.data = args; - } - - return wwatch.openWindow(parent, url, target, features, argstring); - } - - // Pass an array to avoid the browser "|"-splitting behavior. - var argArray = Components.classes["@mozilla.org/supports-array;1"] - .createInstance(Components.interfaces.nsISupportsArray); - - // add args to the arguments array - var stringArgs = null; - if (args instanceof Array) // array - stringArgs = args; - else if (args) // string - stringArgs = [args]; - - if (stringArgs) { - // put the URIs into argArray - var uriArray = Components.classes["@mozilla.org/supports-array;1"] - .createInstance(Components.interfaces.nsISupportsArray); - stringArgs.forEach(function (uri) { - var sstring = Components.classes["@mozilla.org/supports-string;1"] - .createInstance(nsISupportsString); - sstring.data = uri; - uriArray.AppendElement(sstring); - }); - argArray.AppendElement(uriArray); - } else { - argArray.AppendElement(null); - } - - // Pass these as null to ensure that we always trigger the "single URL" - // behavior in browser.js's gBrowserInit.onLoad (which handles the window - // arguments) - argArray.AppendElement(null); // charset - argArray.AppendElement(null); // referer - argArray.AppendElement(null); // postData - argArray.AppendElement(null); // allowThirdPartyFixup - - return wwatch.openWindow(parent, url, target, features, argArray); -} - -function openPreferences() { - var features = "chrome,titlebar,toolbar,centerscreen,dialog=no"; - var url = "chrome://browser/content/preferences/preferences.xul"; - - var win = getMostRecentWindow("Browser:Preferences"); - if (win) { - win.focus(); - } else { - openWindow(null, url, "_blank", features); - } -} - -function getMostRecentWindow(aType) { - var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] - .getService(nsIWindowMediator); - return wm.getMostRecentWindow(aType); -} - -function doSearch(searchTerm, cmdLine) { - var ss = Components.classes["@mozilla.org/browser/search-service;1"] - .getService(nsIBrowserSearchService); - - var submission = ss.defaultEngine.getSubmission(searchTerm); - - // fill our nsISupportsArray with uri-as-wstring, null, null, postData - var sa = Components.classes["@mozilla.org/supports-array;1"] - .createInstance(Components.interfaces.nsISupportsArray); - - var wuri = Components.classes["@mozilla.org/supports-string;1"] - .createInstance(Components.interfaces.nsISupportsString); - wuri.data = submission.uri.spec; - - sa.AppendElement(wuri); - sa.AppendElement(null); - sa.AppendElement(null); - sa.AppendElement(submission.postData); - - // XXXbsmedberg: use handURIToExistingBrowser to obey tabbed-browsing - // preferences, but need nsIBrowserDOMWindow extensions - - var wwatch = Components.classes["@mozilla.org/embedcomp/window-watcher;1"] - .getService(nsIWindowWatcher); - - return wwatch.openWindow(null, gBrowserContentHandler.chromeURL, - "_blank", - "chrome,dialog=no,all" + - gBrowserContentHandler.getFeatures(cmdLine), - sa); -} - -function nsBrowserContentHandler() { -} -nsBrowserContentHandler.prototype = { - classID: Components.ID("{5d0ce354-df01-421a-83fb-7ead0990c24e}"), - - _xpcom_factory: { - createInstance: function bch_factory_ci(outer, iid) { - if (outer) - throw Components.results.NS_ERROR_NO_AGGREGATION; - return gBrowserContentHandler.QueryInterface(iid); - } - }, - - /* helper functions */ - - mChromeURL : null, - - get chromeURL() { - if (this.mChromeURL) { - return this.mChromeURL; - } - - var prefb = Components.classes["@mozilla.org/preferences-service;1"] - .getService(nsIPrefBranch); - this.mChromeURL = prefb.getCharPref("browser.chromeURL"); - - return this.mChromeURL; - }, - - /* nsISupports */ - QueryInterface : XPCOMUtils.generateQI([nsICommandLineHandler, - nsIBrowserHandler, - nsIContentHandler, - nsICommandLineValidator]), - - /* nsICommandLineHandler */ - handle : function bch_handle(cmdLine) { - if (cmdLine.handleFlag("browser", false)) { - // Passing defaultArgs, so use NO_EXTERNAL_URIS - openWindow(null, this.chromeURL, "_blank", - "chrome,dialog=no,all" + this.getFeatures(cmdLine), - this.defaultArgs, NO_EXTERNAL_URIS); - cmdLine.preventDefault = true; - } - - try { - var remoteCommand = cmdLine.handleFlagWithParam("remote", true); - } - catch (e) { - throw NS_ERROR_ABORT; - } - - if (remoteCommand != null) { - try { - var a = /^\s*(\w+)\(([^\)]*)\)\s*$/.exec(remoteCommand); - var remoteVerb; - if (a) { - remoteVerb = a[1].toLowerCase(); - var remoteParams = []; - var sepIndex = a[2].lastIndexOf(","); - if (sepIndex == -1) - remoteParams[0] = a[2]; - else { - remoteParams[0] = a[2].substring(0, sepIndex); - remoteParams[1] = a[2].substring(sepIndex + 1); - } - } - - switch (remoteVerb) { - case "openurl": - case "openfile": - // openURL(<url>) - // openURL(<url>,new-window) - // openURL(<url>,new-tab) - - // First param is the URL, second param (if present) is the "target" - // (tab, window) - var url = remoteParams[0]; - var target = nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW; - if (remoteParams[1]) { - var targetParam = remoteParams[1].toLowerCase() - .replace(/^\s*|\s*$/g, ""); - if (targetParam == "new-tab") - target = nsIBrowserDOMWindow.OPEN_NEWTAB; - else if (targetParam == "new-window") - target = nsIBrowserDOMWindow.OPEN_NEWWINDOW; - else { - // The "target" param isn't one of our supported values, so - // assume it's part of a URL that contains commas. - url += "," + remoteParams[1]; - } - } - - var uri = resolveURIInternal(cmdLine, url); - handURIToExistingBrowser(uri, target, cmdLine); - break; - - case "xfedocommand": - // xfeDoCommand(openBrowser) - if (remoteParams[0].toLowerCase() != "openbrowser") - throw NS_ERROR_ABORT; - - // Passing defaultArgs, so use NO_EXTERNAL_URIS - openWindow(null, this.chromeURL, "_blank", - "chrome,dialog=no,all" + this.getFeatures(cmdLine), - this.defaultArgs, NO_EXTERNAL_URIS); - break; - - default: - // Somebody sent us a remote command we don't know how to process: - // just abort. - throw "Unknown remote command."; - } - - cmdLine.preventDefault = true; - } - catch (e) { - Components.utils.reportError(e); - // If we had a -remote flag but failed to process it, throw - // NS_ERROR_ABORT so that the xremote code knows to return a failure - // back to the handling code. - throw NS_ERROR_ABORT; - } - } - - var uriparam; - try { - while ((uriparam = cmdLine.handleFlagWithParam("new-window", false))) { - var uri = resolveURIInternal(cmdLine, uriparam); - if (!shouldLoadURI(uri)) - continue; - openWindow(null, this.chromeURL, "_blank", - "chrome,dialog=no,all" + this.getFeatures(cmdLine), - uri.spec); - cmdLine.preventDefault = true; - } - } - catch (e) { - Components.utils.reportError(e); - } - - try { - while ((uriparam = cmdLine.handleFlagWithParam("new-tab", false))) { - var uri = resolveURIInternal(cmdLine, uriparam); - handURIToExistingBrowser(uri, nsIBrowserDOMWindow.OPEN_NEWTAB, cmdLine); - cmdLine.preventDefault = true; - } - } - catch (e) { - Components.utils.reportError(e); - } - - var chromeParam = cmdLine.handleFlagWithParam("chrome", false); - if (chromeParam) { - - // Handle the old preference dialog URL separately (bug 285416) - if (chromeParam == "chrome://browser/content/pref/pref.xul") { - openPreferences(); - cmdLine.preventDefault = true; - } else try { - // only load URIs which do not inherit chrome privs - var features = "chrome,dialog=no,all" + this.getFeatures(cmdLine); - var uri = resolveURIInternal(cmdLine, chromeParam); - var netutil = Components.classes["@mozilla.org/network/util;1"] - .getService(nsINetUtil); - if (!netutil.URIChainHasFlags(uri, URI_INHERITS_SECURITY_CONTEXT)) { - openWindow(null, uri.spec, "_blank", features); - cmdLine.preventDefault = true; - } - } - catch (e) { - Components.utils.reportError(e); - } - } - if (cmdLine.handleFlag("preferences", false)) { - openPreferences(); - cmdLine.preventDefault = true; - } - if (cmdLine.handleFlag("silent", false)) - cmdLine.preventDefault = true; - - try { - var privateWindowParam = cmdLine.handleFlagWithParam("private-window", false); - if (privateWindowParam) { - let resolvedURI = resolveURIInternal(cmdLine, privateWindowParam); - handURIToExistingBrowser(resolvedURI, nsIBrowserDOMWindow.OPEN_NEWTAB, cmdLine, true); - cmdLine.preventDefault = true; - } - } catch (e) { - if (e.result != Components.results.NS_ERROR_INVALID_ARG) { - throw e; - } - // NS_ERROR_INVALID_ARG is thrown when flag exists, but has no param. - if (cmdLine.handleFlag("private-window", false)) { - openWindow(null, this.chromeURL, "_blank", - "chrome,dialog=no,private,all" + this.getFeatures(cmdLine), - "about:privatebrowsing"); - cmdLine.preventDefault = true; - } - } - - var searchParam = cmdLine.handleFlagWithParam("search", false); - if (searchParam) { - doSearch(searchParam, cmdLine); - cmdLine.preventDefault = true; - } - - // The global PB Service consumes this flag, so only eat it in per-window - // PB builds. - if (cmdLine.handleFlag("private", false)) { - PrivateBrowsingUtils.enterTemporaryAutoStartMode(); - } - - var fileParam = cmdLine.handleFlagWithParam("file", false); - if (fileParam) { - var file = cmdLine.resolveFile(fileParam); - var ios = Components.classes["@mozilla.org/network/io-service;1"] - .getService(Components.interfaces.nsIIOService); - var uri = ios.newFileURI(file); - openWindow(null, this.chromeURL, "_blank", - "chrome,dialog=no,all" + this.getFeatures(cmdLine), - uri.spec); - cmdLine.preventDefault = true; - } - -#ifdef XP_WIN - // Handle "? searchterm" for Windows Vista start menu integration - for (var i = cmdLine.length - 1; i >= 0; --i) { - var param = cmdLine.getArgument(i); - if (param.match(/^\? /)) { - cmdLine.removeArguments(i, i); - cmdLine.preventDefault = true; - - searchParam = param.substr(2); - doSearch(searchParam, cmdLine); - } - } -#endif - }, - - helpInfo : " --browser Open a browser window.\n" + - " --new-window <url> Open <url> in a new window.\n" + - " --new-tab <url> Open <url> in a new tab.\n" + - " --private-window <url> Open <url> in a new private window.\n" + -#ifdef XP_WIN - " --preferences Open Options dialog.\n" + -#else - " --preferences Open Preferences dialog.\n" + -#endif - " --search <term> Search <term> with your default search engine.\n", - - /* nsIBrowserHandler */ - - get defaultArgs() { - var prefb = Components.classes["@mozilla.org/preferences-service;1"] - .getService(nsIPrefBranch); - - if (!gFirstWindow) { - gFirstWindow = true; - if (PrivateBrowsingUtils.isInTemporaryAutoStartMode) { - return "about:privatebrowsing"; - } - } - - var overridePage = ""; - var haveUpdateSession = false; - try { - // Read the old value of homepage_override.mstone before - // needHomepageOverride updates it, so that we can later add it to the - // URL if we do end up showing an overridePage. This makes it possible - // to have the overridePage's content vary depending on the version we're - // upgrading from. - let old_mstone = Services.prefs.getCharPref("browser.startup.homepage_override.mstone", "unknown"); - let override = needHomepageOverride(prefb); - if (override != OVERRIDE_NONE) { - switch (override) { - case OVERRIDE_NEW_PROFILE: - // New profile. - overridePage = Services.urlFormatter.formatURLPref("startup.homepage_welcome_url"); - break; - case OVERRIDE_NEW_MSTONE: - // Check whether we have a session to restore. If we do, we assume - // that this is an "update" session. - var ss = Components.classes["@mozilla.org/browser/sessionstartup;1"] - .getService(Components.interfaces.nsISessionStartup); - haveUpdateSession = ss.doRestore(); - overridePage = Services.urlFormatter.formatURLPref("startup.homepage_override_url"); - if (prefb.prefHasUserValue("app.update.postupdate")) - overridePage = getPostUpdateOverridePage(overridePage); - - overridePage = overridePage.replace("%OLD_VERSION%", old_mstone); - break; - } - } - } catch (ex) {} - - // formatURLPref might return "about:blank" if getting the pref fails - if (overridePage == "about:blank") - overridePage = ""; - - var startPage = ""; - try { - var choice = prefb.getIntPref("browser.startup.page"); - if (choice == 1 || choice == 3) - startPage = this.startPage; - } catch (e) { - Components.utils.reportError(e); - } - - // Only show the startPage if we're not restoring an update session. - if (overridePage && startPage && !haveUpdateSession) - return overridePage + "|" + startPage; - - return overridePage || startPage || "about:logopage"; - }, - - get startPage() { - var uri = Services.prefs.getComplexValue("browser.startup.homepage", - nsIPrefLocalizedString).data; - if (!uri) { - Services.prefs.clearUserPref("browser.startup.homepage"); - uri = Services.prefs.getComplexValue("browser.startup.homepage", - nsIPrefLocalizedString).data; - } - return uri; - }, - - mFeatures : null, - - getFeatures : function bch_features(cmdLine) { - if (this.mFeatures === null) { - this.mFeatures = ""; - - try { - var width = cmdLine.handleFlagWithParam("width", false); - var height = cmdLine.handleFlagWithParam("height", false); - - if (width) - this.mFeatures += ",width=" + width; - if (height) - this.mFeatures += ",height=" + height; - } - catch (e) { - } - - // The global PB Service consumes this flag, so only eat it in per-window - // PB builds. - if (PrivateBrowsingUtils.isInTemporaryAutoStartMode) { - this.mFeatures = ",private"; - } - } - - return this.mFeatures; - }, - - /* nsIContentHandler */ - - handleContent : function bch_handleContent(contentType, context, request) { - try { - var webNavInfo = Components.classes["@mozilla.org/webnavigation-info;1"] - .getService(nsIWebNavigationInfo); - if (!webNavInfo.isTypeSupported(contentType, null)) { - throw NS_ERROR_WONT_HANDLE_CONTENT; - } - } catch (e) { - throw NS_ERROR_WONT_HANDLE_CONTENT; - } - - request.QueryInterface(nsIChannel); - handURIToExistingBrowser(request.URI, - nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW, null); - request.cancel(NS_BINDING_ABORTED); - }, - - /* nsICommandLineValidator */ - validate : function bch_validate(cmdLine) { - // Other handlers may use osint so only handle the osint flag if the url - // flag is also present and the command line is valid. - var osintFlagIdx = cmdLine.findFlag("osint", false); - var urlFlagIdx = cmdLine.findFlag("url", false); - if (urlFlagIdx > -1 && (osintFlagIdx > -1 || - cmdLine.state == nsICommandLine.STATE_REMOTE_EXPLICIT)) { - var urlParam = cmdLine.getArgument(urlFlagIdx + 1); - if (cmdLine.length != urlFlagIdx + 2 || /firefoxurl:/.test(urlParam)) - throw NS_ERROR_ABORT; - cmdLine.handleFlag("osint", false) - } - }, -}; -var gBrowserContentHandler = new nsBrowserContentHandler(); - -function handURIToExistingBrowser(uri, location, cmdLine, forcePrivate) -{ - if (!shouldLoadURI(uri)) - return; - - // Unless using a private window is forced, open external links in private - // windows only if we're in perma-private mode. - var allowPrivate = forcePrivate || PrivateBrowsingUtils.permanentPrivateBrowsing; - var navWin = RecentWindow.getMostRecentBrowserWindow({private: allowPrivate}); - if (!navWin) { - // if we couldn't load it in an existing window, open a new one - var features = "chrome,dialog=no,all" + gBrowserContentHandler.getFeatures(cmdLine); - if (forcePrivate) { - features += ",private"; - } - openWindow(null, gBrowserContentHandler.chromeURL, "_blank", features, uri.spec); - return; - } - - var navNav = navWin.QueryInterface(nsIInterfaceRequestor) - .getInterface(nsIWebNavigation); - var rootItem = navNav.QueryInterface(nsIDocShellTreeItem).rootTreeItem; - var rootWin = rootItem.QueryInterface(nsIInterfaceRequestor) - .getInterface(nsIDOMWindow); - var bwin = rootWin.QueryInterface(nsIDOMChromeWindow).browserDOMWindow; - bwin.openURI(uri, null, location, - nsIBrowserDOMWindow.OPEN_EXTERNAL); -} - -function nsDefaultCommandLineHandler() { -} - -nsDefaultCommandLineHandler.prototype = { - classID: Components.ID("{47cd0651-b1be-4a0f-b5c4-10e5a573ef71}"), - - /* nsISupports */ - QueryInterface : function dch_QI(iid) { - if (!iid.equals(nsISupports) && - !iid.equals(nsICommandLineHandler)) - throw Components.results.NS_ERROR_NO_INTERFACE; - - return this; - }, - -#ifdef XP_WIN - _haveProfile: false, -#endif - - /* nsICommandLineHandler */ - handle : function dch_handle(cmdLine) { - var urilist = []; - -#ifdef XP_WIN - // If we don't have a profile selected yet (e.g. the Profile Manager is - // displayed) we will crash if we open an url and then select a profile. To - // prevent this handle all url command line flags and set the command line's - // preventDefault to true to prevent the display of the ui. The initial - // command line will be retained when nsAppRunner calls LaunchChild though - // urls launched after the initial launch will be lost. - if (!this._haveProfile) { - try { - // This will throw when a profile has not been selected. - var fl = Components.classes["@mozilla.org/file/directory_service;1"] - .getService(Components.interfaces.nsIProperties); - var dir = fl.get("ProfD", Components.interfaces.nsILocalFile); - this._haveProfile = true; - } - catch (e) { - while ((ar = cmdLine.handleFlagWithParam("url", false))) { } - cmdLine.preventDefault = true; - } - } -#endif - - try { - var ar; - while ((ar = cmdLine.handleFlagWithParam("url", false))) { - var uri = resolveURIInternal(cmdLine, ar); - urilist.push(uri); - } - } - catch (e) { - Components.utils.reportError(e); - } - - let count = cmdLine.length; - - for (let i = 0; i < count; ++i) { - var curarg = cmdLine.getArgument(i); - if (curarg.match(/^-/)) { - Components.utils.reportError("Warning: unrecognized command line flag " + curarg + "\n"); - // To emulate the pre-nsICommandLine behavior, we ignore - // the argument after an unrecognized flag. - ++i; - } else { - try { - urilist.push(resolveURIInternal(cmdLine, curarg)); - } - catch (e) { - Components.utils.reportError("Error opening URI '" + curarg + "' from the command line: " + e + "\n"); - } - } - } - - if (urilist.length) { - if (cmdLine.state != nsICommandLine.STATE_INITIAL_LAUNCH && - urilist.length == 1) { - // Try to find an existing window and load our URI into the - // current tab, new tab, or new window as prefs determine. - try { - handURIToExistingBrowser(urilist[0], nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW, cmdLine); - return; - } - catch (e) { - } - } - - var URLlist = urilist.filter(shouldLoadURI).map(function (u) u.spec); - if (URLlist.length) { - openWindow(null, gBrowserContentHandler.chromeURL, "_blank", - "chrome,dialog=no,all" + gBrowserContentHandler.getFeatures(cmdLine), - URLlist); - } - - } - else if (!cmdLine.preventDefault) { - // Passing defaultArgs, so use NO_EXTERNAL_URIS - openWindow(null, gBrowserContentHandler.chromeURL, "_blank", - "chrome,dialog=no,all" + gBrowserContentHandler.getFeatures(cmdLine), - gBrowserContentHandler.defaultArgs, NO_EXTERNAL_URIS); - } - }, - - helpInfo : "", -}; - -var components = [nsBrowserContentHandler, nsDefaultCommandLineHandler]; -this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components); diff --git a/components/nsBrowserGlue.js b/components/nsBrowserGlue.js deleted file mode 100644 index e8eefa4..0000000 --- a/components/nsBrowserGlue.js +++ /dev/null @@ -1,1867 +0,0 @@ -# -*- indent-tabs-mode: nil -*- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -const Ci = Components.interfaces; -const Cc = Components.classes; -const Cr = Components.results; -const Cu = Components.utils; - -const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -// Define Lazy Service Getters -XPCOMUtils.defineLazyServiceGetter(this, "AlertsService", - "@mozilla.org/alerts-service;1", "nsIAlertsService"); - -// Define Lazy Module Getters -[ - ["AddonManager", "resource://gre/modules/AddonManager.jsm"], - ["NetUtil", "resource://gre/modules/NetUtil.jsm"], - ["UserAgentOverrides", "resource://gre/modules/UserAgentOverrides.jsm"], - ["FileUtils", "resource://gre/modules/FileUtils.jsm"], - ["PlacesUtils", "resource://gre/modules/PlacesUtils.jsm"], - ["BookmarkHTMLUtils", "resource://gre/modules/BookmarkHTMLUtils.jsm"], - ["BookmarkJSONUtils", "resource://gre/modules/BookmarkJSONUtils.jsm"], - ["PageThumbs", "resource://gre/modules/PageThumbs.jsm"], - ["NewTabUtils", "resource://gre/modules/NewTabUtils.jsm"], - ["BrowserNewTabPreloader", "resource:///modules/BrowserNewTabPreloader.jsm"], -#ifdef MOZ_WEBRTC - ["webrtcUI", "resource:///modules/webrtcUI.jsm"], -#endif - ["PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm"], - ["RecentWindow", "resource:///modules/RecentWindow.jsm"], - ["Task", "resource://gre/modules/Task.jsm"], - ["PlacesBackups", "resource://gre/modules/PlacesBackups.jsm"], - ["OS", "resource://gre/modules/osfile.jsm"], - ["LoginManagerParent", "resource://gre/modules/LoginManagerParent.jsm"], - ["FormValidationHandler", "resource:///modules/FormValidationHandler.jsm"], - ["AutoCompletePopup", "resource:///modules/AutoCompletePopup.jsm"], - ["DateTimePickerHelper", "resource://gre/modules/DateTimePickerHelper.jsm"], - ["ShellService", "resource:///modules/ShellService.jsm"], -].forEach(([name, resource]) => XPCOMUtils.defineLazyModuleGetter(this, name, resource)); - -// Define Lazy Getters - -XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() { - return Services.strings.createBundle('chrome://branding/locale/brand.properties'); -}); - -XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() { - return Services.strings.createBundle('chrome://browser/locale/browser.properties'); -}); - -const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser"; -const PREF_PLUGINS_UPDATEURL = "plugins.update.url"; - -// We try to backup bookmarks at idle times, to avoid doing that at shutdown. -// Number of idle seconds before trying to backup bookmarks. 15 minutes. -const BOOKMARKS_BACKUP_IDLE_TIME = 15 * 60; -// Minimum interval in milliseconds between backups. -const BOOKMARKS_BACKUP_INTERVAL = 86400 * 1000; -// Maximum number of backups to create. Old ones will be purged. -const BOOKMARKS_BACKUP_MAX_BACKUPS = 10; - -// Factory object -const BrowserGlueServiceFactory = { - _instance: null, - createInstance: function BGSF_createInstance(outer, iid) { - if (outer != null) - throw Components.results.NS_ERROR_NO_AGGREGATION; - return this._instance == null ? - this._instance = new BrowserGlue() : this._instance; - } -}; - -// Constructor - -function BrowserGlue() { - XPCOMUtils.defineLazyServiceGetter(this, "_idleService", - "@mozilla.org/widget/idleservice;1", - "nsIIdleService"); - - XPCOMUtils.defineLazyGetter(this, "_distributionCustomizer", function() { - Cu.import("resource:///modules/distribution.js"); - return new DistributionCustomizer(); - }); - - XPCOMUtils.defineLazyGetter(this, "_sanitizer", - function() { - let sanitizerScope = {}; - Services.scriptloader.loadSubScript("chrome://browser/content/sanitize.js", sanitizerScope); - return sanitizerScope.Sanitizer; - }); - - this._init(); -} - -#ifndef XP_MACOSX -# OS X has the concept of zero-window sessions and therefore ignores the -# browser-lastwindow-close-* topics. -#define OBSERVE_LASTWINDOW_CLOSE_TOPICS 1 -#endif - -BrowserGlue.prototype = { - _saveSession: false, - _isIdleObserver: false, - _isPlacesInitObserver: false, - _isPlacesLockedObserver: false, - _isPlacesShutdownObserver: false, - _isPlacesDatabaseLocked: false, - _migrationImportsDefaultBookmarks: false, - - _setPrefToSaveSession: function BG__setPrefToSaveSession(aForce) { - if (!this._saveSession && !aForce) - return; - - Services.prefs.setBoolPref("browser.sessionstore.resume_session_once", true); - - // This method can be called via [NSApplication terminate:] on Mac, which - // ends up causing prefs not to be flushed to disk, so we need to do that - // explicitly here. See bug 497652. - Services.prefs.savePrefFile(null); - }, - -#ifdef MOZ_SERVICES_SYNC - _setSyncAutoconnectDelay: function BG__setSyncAutoconnectDelay() { - // Assume that a non-zero value for services.sync.autoconnectDelay should override - if (Services.prefs.prefHasUserValue("services.sync.autoconnectDelay")) { - let prefDelay = Services.prefs.getIntPref("services.sync.autoconnectDelay"); - - if (prefDelay > 0) - return; - } - - // delays are in seconds - const MAX_DELAY = 300; - let delay = 3; - let browserEnum = Services.wm.getEnumerator("navigator:browser"); - while (browserEnum.hasMoreElements()) { - delay += browserEnum.getNext().gBrowser.tabs.length; - } - delay = delay <= MAX_DELAY ? delay : MAX_DELAY; - - Cu.import("resource://services-sync/main.js"); - Weave.Service.scheduler.delayedAutoConnect(delay); - }, -#endif - - // nsIObserver implementation - observe: function BG_observe(subject, topic, data) { - switch (topic) { - case "notifications-open-settings": - this._openPermissions(subject); - break; - case "prefservice:after-app-defaults": - this._onAppDefaults(); - break; - case "final-ui-startup": - this._finalUIStartup(); - break; - case "browser-delayed-startup-finished": - this._onFirstWindowLoaded(); - Services.obs.removeObserver(this, "browser-delayed-startup-finished"); - break; - case "sessionstore-windows-restored": - this._onWindowsRestored(); - break; - case "browser:purge-session-history": - // reset the console service's error buffer - Services.console.logStringMessage(null); // clear the console (in case it's open) - Services.console.reset(); - break; - case "quit-application-requested": - this._onQuitRequest(subject, data); - break; - case "quit-application-granted": - // This pref must be set here because SessionStore will use its value - // on quit-application. - this._setPrefToSaveSession(); - try { - let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]. - getService(Ci.nsIAppStartup); - appStartup.trackStartupCrashEnd(); - } catch (e) { - Cu.reportError("Could not end startup crash tracking in quit-application-granted: " + e); - } - DateTimePickerHelper.uninit(); - break; -#ifdef OBSERVE_LASTWINDOW_CLOSE_TOPICS - case "browser-lastwindow-close-requested": - // The application is not actually quitting, but the last full browser - // window is about to be closed. - this._onQuitRequest(subject, "lastwindow"); - break; - case "browser-lastwindow-close-granted": - this._setPrefToSaveSession(); - break; -#endif -#ifdef MOZ_SERVICES_SYNC - case "weave:service:ready": - this._setSyncAutoconnectDelay(); - break; - case "weave:engine:clients:display-uri": - this._onDisplaySyncURI(subject); - break; -#endif - case "session-save": - this._setPrefToSaveSession(true); - subject.QueryInterface(Ci.nsISupportsPRBool); - subject.data = true; - break; - case "places-init-complete": - if (!this._migrationImportsDefaultBookmarks) - this._initPlaces(false); - - Services.obs.removeObserver(this, "places-init-complete"); - this._isPlacesInitObserver = false; - // no longer needed, since history was initialized completely. - Services.obs.removeObserver(this, "places-database-locked"); - this._isPlacesLockedObserver = false; - break; - case "places-database-locked": - this._isPlacesDatabaseLocked = true; - // Stop observing, so further attempts to load history service - // will not show the prompt. - Services.obs.removeObserver(this, "places-database-locked"); - this._isPlacesLockedObserver = false; - break; - case "places-shutdown": - if (this._isPlacesShutdownObserver) { - Services.obs.removeObserver(this, "places-shutdown"); - this._isPlacesShutdownObserver = false; - } - // places-shutdown is fired when the profile is about to disappear. - this._onPlacesShutdown(); - break; - case "idle": - if ((this._idleService.idleTime > BOOKMARKS_BACKUP_IDLE_TIME * 1000) && - this._shouldBackupBookmarks()) - this._backupBookmarks(); - break; - case "distribution-customization-complete": - Services.obs.removeObserver(this, "distribution-customization-complete"); - // Customization has finished, we don't need the customizer anymore. - delete this._distributionCustomizer; - break; - case "browser-glue-test": // used by tests - if (data == "post-update-notification") { - if (Services.prefs.prefHasUserValue("app.update.postupdate")) - this._showUpdateNotification(); - } - else if (data == "force-ui-migration") { - this._migrateUI(); - } - else if (data == "force-distribution-customization") { - this._distributionCustomizer.applyPrefDefaults(); - this._distributionCustomizer.applyCustomizations(); - // To apply distribution bookmarks use "places-init-complete". - } - else if (data == "force-places-init") { - this._initPlaces(false); - } - break; - case "initial-migration-will-import-default-bookmarks": - this._migrationImportsDefaultBookmarks = true; - break; - case "initial-migration-did-import-default-bookmarks": - this._initPlaces(true); - break; - case "handle-xul-text-link": - let linkHandled = subject.QueryInterface(Ci.nsISupportsPRBool); - if (!linkHandled.data) { - let win = this.getMostRecentBrowserWindow(); - if (win) { - data = JSON.parse(data); - win.openUILinkIn(data.href, "tab"); - linkHandled.data = true; - } - } - break; - case "profile-before-change": - this._onProfileShutdown(); - break; - case "browser-search-engine-modified": - if (data != "engine-default" && data != "engine-current") { - break; - } - // Enforce that the search service's defaultEngine is always equal to - // its currentEngine. The search service will notify us any time either - // of them are changed (either by directly setting the relevant prefs, - // i.e. if add-ons try to change this directly, or if the - // nsIBrowserSearchService setters are called). - // No need to initialize the search service, since it's guaranteed to be - // initialized already when this notification fires. - let ss = Services.search; - if (ss.currentEngine.name == ss.defaultEngine.name) - return; - if (data == "engine-current") - ss.defaultEngine = ss.currentEngine; - else - ss.currentEngine = ss.defaultEngine; - break; - case "browser-search-service": - if (data != "init-complete") - return; - Services.obs.removeObserver(this, "browser-search-service"); - this._syncSearchEngines(); - break; - } - }, - - _syncSearchEngines: function () { - // Only do this if the search service is already initialized. This function - // gets called in finalUIStartup and from a browser-search-service observer, - // to catch both cases (search service initialization occurring before and - // after final-ui-startup) - if (Services.search.isInitialized) { - Services.search.defaultEngine = Services.search.currentEngine; - } - }, - - // initialization (called on application startup) - _init: function BG__init() { - let os = Services.obs; - os.addObserver(this, "notifications-open-settings", false); - os.addObserver(this, "prefservice:after-app-defaults", false); - os.addObserver(this, "final-ui-startup", false); - os.addObserver(this, "browser-delayed-startup-finished", false); - os.addObserver(this, "sessionstore-windows-restored", false); - os.addObserver(this, "browser:purge-session-history", false); - os.addObserver(this, "quit-application-requested", false); - os.addObserver(this, "quit-application-granted", false); -#ifdef OBSERVE_LASTWINDOW_CLOSE_TOPICS - os.addObserver(this, "browser-lastwindow-close-requested", false); - os.addObserver(this, "browser-lastwindow-close-granted", false); -#endif -#ifdef MOZ_SERVICES_SYNC - os.addObserver(this, "weave:service:ready", false); - os.addObserver(this, "weave:engine:clients:display-uri", false); -#endif - os.addObserver(this, "session-save", false); - os.addObserver(this, "places-init-complete", false); - this._isPlacesInitObserver = true; - os.addObserver(this, "places-database-locked", false); - this._isPlacesLockedObserver = true; - os.addObserver(this, "distribution-customization-complete", false); - os.addObserver(this, "places-shutdown", false); - this._isPlacesShutdownObserver = true; - os.addObserver(this, "handle-xul-text-link", false); - os.addObserver(this, "profile-before-change", false); - os.addObserver(this, "browser-search-engine-modified", false); - os.addObserver(this, "browser-search-service", false); - }, - - // cleanup (called on application shutdown) - _dispose: function BG__dispose() { - let os = Services.obs; - os.removeObserver(this, "notifications-open-settings"); - os.removeObserver(this, "prefservice:after-app-defaults"); - os.removeObserver(this, "final-ui-startup"); - os.removeObserver(this, "sessionstore-windows-restored"); - os.removeObserver(this, "browser:purge-session-history"); - os.removeObserver(this, "quit-application-requested"); - os.removeObserver(this, "quit-application-granted"); -#ifdef OBSERVE_LASTWINDOW_CLOSE_TOPICS - os.removeObserver(this, "browser-lastwindow-close-requested"); - os.removeObserver(this, "browser-lastwindow-close-granted"); -#endif -#ifdef MOZ_SERVICES_SYNC - os.removeObserver(this, "weave:service:ready"); - os.removeObserver(this, "weave:engine:clients:display-uri"); -#endif - os.removeObserver(this, "session-save"); - if (this._isIdleObserver) - this._idleService.removeIdleObserver(this, BOOKMARKS_BACKUP_IDLE_TIME); - if (this._isPlacesInitObserver) - os.removeObserver(this, "places-init-complete"); - if (this._isPlacesLockedObserver) - os.removeObserver(this, "places-database-locked"); - if (this._isPlacesShutdownObserver) - os.removeObserver(this, "places-shutdown"); - os.removeObserver(this, "handle-xul-text-link"); - os.removeObserver(this, "profile-before-change"); - os.removeObserver(this, "browser-search-engine-modified"); - try { - os.removeObserver(this, "browser-search-service"); - // may have already been removed by the observer - } catch (ex) {} - }, - - _onAppDefaults: function BG__onAppDefaults() { - // apply distribution customizations (prefs) - // other customizations are applied in _finalUIStartup() - this._distributionCustomizer.applyPrefDefaults(); - }, - - // runs on startup, before the first command line handler is invoked - // (i.e. before the first window is opened) - _finalUIStartup: function BG__finalUIStartup() { - this._sanitizer.onStartup(); - // check if we're in safe mode - if (Services.appinfo.inSafeMode) { - Services.ww.openWindow(null, "chrome://browser/content/safeMode.xul", - "_blank", "chrome,centerscreen,modal,resizable=no", null); - } - - // apply distribution customizations - // prefs are applied in _onAppDefaults() - this._distributionCustomizer.applyCustomizations(); - - // handle any UI migration - this._migrateUI(); - - this._setUpUserAgentOverrides(); - - this._syncSearchEngines(); - - PageThumbs.init(); - NewTabUtils.init(); - BrowserNewTabPreloader.init(); -#ifdef MOZ_WEBRTC - webrtcUI.init(); -#endif - FormValidationHandler.init(); - - AutoCompletePopup.init(); - - LoginManagerParent.init(); - - Services.obs.notifyObservers(null, "browser-ui-startup-complete", ""); - }, - - _setUpUserAgentOverrides: function BG__setUpUserAgentOverrides() { - UserAgentOverrides.init(); - - if (Services.prefs.getBoolPref("general.useragent.complexOverride.moodle")) { - UserAgentOverrides.addComplexOverride(function (aHttpChannel, aOriginalUA) { - let cookies; - try { - cookies = aHttpChannel.getRequestHeader("Cookie"); - } catch (e) { /* no cookie sent */ } - if (cookies && cookies.indexOf("MoodleSession") > -1) - return aOriginalUA.replace(/Goanna\/[^ ]*/, "Goanna/20100101"); - return null; - }); - } - }, - - _trackSlowStartup: function () { - if (Services.startup.interrupted || - Services.prefs.getBoolPref("browser.slowStartup.notificationDisabled")) - return; - - let currentTime = Date.now() - Services.startup.getStartupInfo().process; - let averageTime = 0; - let samples = 0; - try { - averageTime = Services.prefs.getIntPref("browser.slowStartup.averageTime"); - samples = Services.prefs.getIntPref("browser.slowStartup.samples"); - } catch (e) { } - - averageTime = (averageTime * samples + currentTime) / ++samples; - - if (samples >= Services.prefs.getIntPref("browser.slowStartup.maxSamples")) { - if (averageTime > Services.prefs.getIntPref("browser.slowStartup.timeThreshold")) - this._showSlowStartupNotification(); - averageTime = 0; - samples = 0; - } - - Services.prefs.setIntPref("browser.slowStartup.averageTime", averageTime); - Services.prefs.setIntPref("browser.slowStartup.samples", samples); - }, - - _showSlowStartupNotification: function () { - let win = this.getMostRecentBrowserWindow(); - if (!win) - return; - - let productName = gBrandBundle.GetStringFromName("brandFullName"); - let message = win.gNavigatorBundle.getFormattedString("slowStartup.message", [productName]); - - let buttons = [ - { - label: win.gNavigatorBundle.getString("slowStartup.helpButton.label"), - accessKey: win.gNavigatorBundle.getString("slowStartup.helpButton.accesskey"), - callback: function () { - win.openUILinkIn(Services.prefs.getCharPref("browser.slowstartup.help.url"), "tab"); - } - }, - { - label: win.gNavigatorBundle.getString("slowStartup.disableNotificationButton.label"), - accessKey: win.gNavigatorBundle.getString("slowStartup.disableNotificationButton.accesskey"), - callback: function () { - Services.prefs.setBoolPref("browser.slowStartup.notificationDisabled", true); - } - } - ]; - - let nb = win.document.getElementById("global-notificationbox"); - nb.appendNotification(message, "slow-startup", - "chrome://browser/skin/slowStartup-16.png", - nb.PRIORITY_INFO_LOW, buttons); - }, - - // the first browser window has finished initializing - _onFirstWindowLoaded: function BG__onFirstWindowLoaded() { -#ifdef XP_WIN - // For windows seven, initialize the jump list module. - const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1"; - if (WINTASKBAR_CONTRACTID in Cc && - Cc[WINTASKBAR_CONTRACTID].getService(Ci.nsIWinTaskbar).available) { - let temp = {}; - Cu.import("resource:///modules/WindowsJumpLists.jsm", temp); - temp.WinTaskbarJumpList.startup(); - } -#endif - - DateTimePickerHelper.init(); - - this._trackSlowStartup(); - }, - - /** - * Profile shutdown handler (contains profile cleanup routines). - * All components depending on Places should be shut down in - * _onPlacesShutdown() and not here. - */ - _onProfileShutdown: function BG__onProfileShutdown() { - BrowserNewTabPreloader.uninit(); - UserAgentOverrides.uninit(); -#ifdef MOZ_WEBRTC - webrtcUI.uninit(); -#endif - FormValidationHandler.uninit(); - AutoCompletePopup.uninit(); - this._dispose(); - }, - - // All initial windows have opened. - _onWindowsRestored: function BG__onWindowsRestored() { - // Show update notification, if needed. - if (Services.prefs.prefHasUserValue("app.update.postupdate")) - this._showUpdateNotification(); - - // Load the "more info" page for a locked places.sqlite - // This property is set earlier by places-database-locked topic. - if (this._isPlacesDatabaseLocked) { - this._showPlacesLockedNotificationBox(); - } - - // For any add-ons that were installed disabled and can be enabled offer - // them to the user. - let changedIDs = AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_INSTALLED); - if (changedIDs.length > 0) { - let win = this.getMostRecentBrowserWindow(); - AddonManager.getAddonsByIDs(changedIDs, function(aAddons) { - aAddons.forEach(function(aAddon) { - // If the add-on isn't user disabled or can't be enabled then skip it. - if (!aAddon.userDisabled || !(aAddon.permissions & AddonManager.PERM_CAN_ENABLE)) - return; - - win.openUILinkIn("about:newaddon?id=" + aAddon.id, "tab"); - }) - }); - } - - // Perform default browser checking. - if (ShellService) { - let shouldCheck = ShellService.shouldCheckDefaultBrowser; - - const skipDefaultBrowserCheck = - Services.prefs.getBoolPref("browser.shell.skipDefaultBrowserCheckOnFirstRun") && - Services.prefs.getBoolPref("browser.shell.skipDefaultBrowserCheck"); - - const usePromptLimit = false; - let promptCount = - usePromptLimit ? Services.prefs.getIntPref("browser.shell.defaultBrowserCheckCount") : 0; - - let willRecoverSession = false; - try { - let ss = Cc["@mozilla.org/browser/sessionstartup;1"]. - getService(Ci.nsISessionStartup); - willRecoverSession = - (ss.sessionType == Ci.nsISessionStartup.RECOVER_SESSION); - } - catch (ex) { /* never mind; suppose SessionStore is broken */ } - - // startup check, check all assoc - let isDefault = false; - let isDefaultError = false; - try { - isDefault = ShellService.isDefaultBrowser(true, false); - } catch (ex) { - isDefaultError = true; - } - - if (isDefault) { - let now = (Math.floor(Date.now() / 1000)).toString(); - Services.prefs.setCharPref("browser.shell.mostRecentDateSetAsDefault", now); - } - - let willPrompt = shouldCheck && !isDefault && !willRecoverSession; - - // Skip the "Set Default Browser" check during first-run or after the - // browser has been run a few times. - if (willPrompt) { - Services.tm.mainThread.dispatch(function() { - var win = this.getMostRecentBrowserWindow(); - var brandBundle = win.document.getElementById("bundle_brand"); - var shellBundle = win.document.getElementById("bundle_shell"); - - var brandShortName = brandBundle.getString("brandShortName"); - var promptTitle = shellBundle.getString("setDefaultBrowserTitle"); - var promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage", - [brandShortName]); - var checkboxLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk", - [brandShortName]); - var checkEveryTime = { value: shouldCheck }; - var ps = Services.prompt; - var rv = ps.confirmEx(win, promptTitle, promptMessage, - ps.STD_YES_NO_BUTTONS, - null, null, null, checkboxLabel, checkEveryTime); - if (rv == 0) { - var claimAllTypes = true; -#ifdef XP_WIN - try { - // In Windows 8+, the UI for selecting default protocol is much - // nicer than the UI for setting file type associations. So we - // only show the protocol association screen on Windows 8. - // Windows 8 is version 6.2. - let version = Services.sysinfo.getProperty("version"); - claimAllTypes = (parseFloat(version) < 6.2); - } catch (ex) { } -#endif - ShellService.setDefaultBrowser(claimAllTypes, false); - } - ShellService.shouldCheckDefaultBrowser = checkEveryTime.value; - }.bind(this), Ci.nsIThread.DISPATCH_NORMAL); - } - } - }, - - _onQuitRequest: function BG__onQuitRequest(aCancelQuit, aQuitType) { - // If user has already dismissed quit request, then do nothing - if ((aCancelQuit instanceof Ci.nsISupportsPRBool) && aCancelQuit.data) - return; - - // There are several cases where we won't show a dialog here: - // 1. There is only 1 tab open in 1 window - // 2. The session will be restored at startup, indicated by - // browser.startup.page == 3 or browser.sessionstore.resume_session_once == true - // 3. browser.warnOnQuit == false - // 4. The browser is currently in Private Browsing mode - // 5. The browser will be restarted. - // - // Otherwise these are the conditions and the associated dialogs that will be shown: - // 1. aQuitType == "lastwindow" or "quit" and browser.showQuitWarning == true - // - The quit dialog will be shown - // 2. aQuitType == "lastwindow" && browser.tabs.warnOnClose == true - // - The "closing multiple tabs" dialog will be shown - // - // aQuitType == "lastwindow" is overloaded. "lastwindow" is used to indicate - // "the last window is closing but we're not quitting (a non-browser window is open)" - // and also "we're quitting by closing the last window". - - if (aQuitType == "restart") - return; - - var windowcount = 0; - var pagecount = 0; - var browserEnum = Services.wm.getEnumerator("navigator:browser"); - let allWindowsPrivate = true; - while (browserEnum.hasMoreElements()) { - windowcount++; - - var browser = browserEnum.getNext(); - if (!PrivateBrowsingUtils.isWindowPrivate(browser)) - allWindowsPrivate = false; - var tabbrowser = browser.document.getElementById("content"); - if (tabbrowser) - pagecount += tabbrowser.browsers.length - tabbrowser._numPinnedTabs; - } - - this._saveSession = false; - if (pagecount < 2) - return; - - if (!aQuitType) - aQuitType = "quit"; - - // browser.warnOnQuit is a hidden global boolean to override all quit prompts - // browser.showQuitWarning specifically covers quitting - // browser.tabs.warnOnClose is the global "warn when closing multiple tabs" pref - - var sessionWillBeRestored = Services.prefs.getIntPref("browser.startup.page") == 3 || - Services.prefs.getBoolPref("browser.sessionstore.resume_session_once"); - if (sessionWillBeRestored || !Services.prefs.getBoolPref("browser.warnOnQuit")) - return; - - let win = Services.wm.getMostRecentWindow("navigator:browser"); - - // On last window close or quit && showQuitWarning, we want to show the - // quit warning. - if (!Services.prefs.getBoolPref("browser.showQuitWarning")) { - if (aQuitType == "lastwindow") { - // If aQuitType is "lastwindow" and we aren't showing the quit warning, - // we should show the window closing warning instead. warnAboutClosing - // tabs checks browser.tabs.warnOnClose and returns if it's ok to close - // the window. It doesn't actually close the window. - aCancelQuit.data = - !win.gBrowser.warnAboutClosingTabs(win.gBrowser.closingTabsEnum.ALL); - } - return; - } - - let prompt = Services.prompt; - let quitBundle = Services.strings.createBundle("chrome://browser/locale/quitDialog.properties"); - - let appName = gBrandBundle.GetStringFromName("brandShortName"); - let quitDialogTitle = quitBundle.formatStringFromName("quitDialogTitle", - [appName], 1); - let neverAskText = quitBundle.GetStringFromName("neverAsk2"); - let neverAsk = {value: false}; - - let choice; - if (allWindowsPrivate) { - let text = quitBundle.formatStringFromName("messagePrivate", [appName], 1); - let flags = prompt.BUTTON_TITLE_IS_STRING * prompt.BUTTON_POS_0 + - prompt.BUTTON_TITLE_IS_STRING * prompt.BUTTON_POS_1 + - prompt.BUTTON_POS_0_DEFAULT; - choice = prompt.confirmEx(win, quitDialogTitle, text, flags, - quitBundle.GetStringFromName("quitTitle"), - quitBundle.GetStringFromName("cancelTitle"), - null, - neverAskText, neverAsk); - - // The order of the buttons differs between the prompt.confirmEx calls - // here so we need to fix this for proper handling below. - if (choice == 0) { - choice = 2; - } - } else { - let text = quitBundle.formatStringFromName( - windowcount == 1 ? "messageNoWindows" : "message", [appName], 1); - let flags = prompt.BUTTON_TITLE_IS_STRING * prompt.BUTTON_POS_0 + - prompt.BUTTON_TITLE_IS_STRING * prompt.BUTTON_POS_1 + - prompt.BUTTON_TITLE_IS_STRING * prompt.BUTTON_POS_2 + - prompt.BUTTON_POS_0_DEFAULT; - choice = prompt.confirmEx(win, quitDialogTitle, text, flags, - quitBundle.GetStringFromName("saveTitle"), - quitBundle.GetStringFromName("cancelTitle"), - quitBundle.GetStringFromName("quitTitle"), - neverAskText, neverAsk); - } - - switch (choice) { - case 2: // Quit - if (neverAsk.value) - Services.prefs.setBoolPref("browser.showQuitWarning", false); - break; - case 1: // Cancel - aCancelQuit.QueryInterface(Ci.nsISupportsPRBool); - aCancelQuit.data = true; - break; - case 0: // Save & Quit - this._saveSession = true; - if (neverAsk.value) { - // always save state when shutting down - Services.prefs.setIntPref("browser.startup.page", 3); - } - break; - } - }, - - _showUpdateNotification: function BG__showUpdateNotification() { - Services.prefs.clearUserPref("app.update.postupdate"); - - var um = Cc["@mozilla.org/updates/update-manager;1"]. - getService(Ci.nsIUpdateManager); - try { - // If the updates.xml file is deleted then getUpdateAt will throw. - var update = um.getUpdateAt(0).QueryInterface(Ci.nsIPropertyBag); - } - catch (e) { - // This should never happen. - Cu.reportError("Unable to find update: " + e); - return; - } - - var actions = update.getProperty("actions"); - if (!actions || actions.indexOf("silent") != -1) - return; - - var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"]. - getService(Ci.nsIURLFormatter); - var appName = gBrandBundle.GetStringFromName("brandShortName"); - - function getNotifyString(aPropData) { - var propValue = update.getProperty(aPropData.propName); - if (!propValue) { - if (aPropData.prefName) - propValue = formatter.formatURLPref(aPropData.prefName); - else if (aPropData.stringParams) - propValue = gBrowserBundle.formatStringFromName(aPropData.stringName, - aPropData.stringParams, - aPropData.stringParams.length); - else - propValue = gBrowserBundle.GetStringFromName(aPropData.stringName); - } - return propValue; - } - - if (actions.indexOf("showNotification") != -1) { - let text = getNotifyString({propName: "notificationText", - stringName: "puNotifyText", - stringParams: [appName]}); - let url = getNotifyString({propName: "notificationURL", - prefName: "startup.homepage_override_url"}); - let label = getNotifyString({propName: "notificationButtonLabel", - stringName: "pu.notifyButton.label"}); - let key = getNotifyString({propName: "notificationButtonAccessKey", - stringName: "pu.notifyButton.accesskey"}); - - let win = this.getMostRecentBrowserWindow(); - let notifyBox = win.gBrowser.getNotificationBox(); - - let buttons = [ - { - label: label, - accessKey: key, - popup: null, - callback: function(aNotificationBar, aButton) { - win.openUILinkIn(url, "tab"); - } - } - ]; - - let notification = notifyBox.appendNotification(text, "post-update-notification", - null, notifyBox.PRIORITY_INFO_LOW, - buttons); - notification.persistence = -1; // Until user closes it - } - - if (actions.indexOf("showAlert") == -1) - return; - - let title = getNotifyString({propName: "alertTitle", - stringName: "puAlertTitle", - stringParams: [appName]}); - let text = getNotifyString({propName: "alertText", - stringName: "puAlertText", - stringParams: [appName]}); - let url = getNotifyString({propName: "alertURL", - prefName: "startup.homepage_override_url"}); - - var self = this; - function clickCallback(subject, topic, data) { - // This callback will be called twice but only once with this topic - if (topic != "alertclickcallback") - return; - let win = self.getMostRecentBrowserWindow(); - win.openUILinkIn(data, "tab"); - } - - try { - // This will throw NS_ERROR_NOT_AVAILABLE if the notification cannot - // be displayed per the idl. - AlertsService.showAlertNotification(null, title, text, - true, url, clickCallback); - } - catch (e) { - Cu.reportError(e); - } - }, - - _showPluginUpdatePage: function BG__showPluginUpdatePage() { - // Pale Moon: disable this functionality from BrowserGlue, people are - // already notified if they visit a page with an outdated plugin, and - // they can check properly from the plugins page as well. - -// Services.prefs.setBoolPref(PREF_PLUGINS_NOTIFYUSER, false); -// -// var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"]. -// getService(Ci.nsIURLFormatter); -// var updateUrl = formatter.formatURLPref(PREF_PLUGINS_UPDATEURL); -// -// var win = this.getMostRecentBrowserWindow(); -// win.openUILinkIn(updateUrl, "tab"); - }, - - /** - * Initialize Places - * - imports the bookmarks html file if bookmarks database is empty, try to - * restore bookmarks from a JSON/JSONLZ4 backup if the backend indicates - * that the database was corrupt. - * - * These prefs can be set up by the frontend: - * - * WARNING: setting these preferences to true will overwite existing bookmarks - * - * - browser.places.importBookmarksHTML - * Set to true will import the bookmarks.html file from the profile folder. - * - browser.places.smartBookmarksVersion - * Set during HTML import to indicate that Smart Bookmarks were created. - * Set to -1 to disable Smart Bookmarks creation. - * Set to 0 to restore current Smart Bookmarks. - * - browser.bookmarks.restore_default_bookmarks - * Set to true by safe-mode dialog to indicate we must restore default - * bookmarks. - */ - _initPlaces: function BG__initPlaces(aInitialMigrationPerformed) { - // We must instantiate the history service since it will tell us if we - // need to import or restore bookmarks due to first-run, corruption or - // forced migration (due to a major schema change). - // If the database is corrupt or has been newly created we should - // import bookmarks. - var dbStatus = PlacesUtils.history.databaseStatus; - var importBookmarks = !aInitialMigrationPerformed && - (dbStatus == PlacesUtils.history.DATABASE_STATUS_CREATE || - dbStatus == PlacesUtils.history.DATABASE_STATUS_CORRUPT); - - // Check if user or an extension has required to import bookmarks.html - var importBookmarksHTML = false; - try { - importBookmarksHTML = - Services.prefs.getBoolPref("browser.places.importBookmarksHTML"); - if (importBookmarksHTML) - importBookmarks = true; - } catch(ex) {} - - Task.spawn(function() { - // Check if Safe Mode or the user has required to restore bookmarks from - // default profile's bookmarks.html - var restoreDefaultBookmarks = false; - try { - restoreDefaultBookmarks = - Services.prefs.getBoolPref("browser.bookmarks.restore_default_bookmarks"); - if (restoreDefaultBookmarks) { - // Ensure that we already have a bookmarks backup for today. - if (this._shouldBackupBookmarks()) - yield this._backupBookmarks(); - importBookmarks = true; - } - } catch(ex) {} - - // If the user did not require to restore default bookmarks, or import - // from bookmarks.html, we will try to restore from JSON/JSONLZ4 - if (importBookmarks && !restoreDefaultBookmarks && !importBookmarksHTML) { - // get latest JSON/JSONLZ4 backup - var bookmarksBackupFile = yield PlacesBackups.getMostRecentBackup(); - if (bookmarksBackupFile) { - // restore from JSON/JSONLZ4 backup - yield BookmarkJSONUtils.importFromFile(bookmarksBackupFile, true); - importBookmarks = false; - } - else { - // We have created a new database but we don't have any backup available - importBookmarks = true; - if (yield OS.File.exists(BookmarkHTMLUtils.defaultPath)) { - // If bookmarks.html is available in current profile import it... - importBookmarksHTML = true; - } - else { - // ...otherwise we will restore defaults - restoreDefaultBookmarks = true; - } - } - } - - // If bookmarks are not imported, then initialize smart bookmarks. This - // happens during a common startup. - // Otherwise, if any kind of import runs, smart bookmarks creation should be - // delayed till the import operations has finished. Not doing so would - // cause them to be overwritten by the newly imported bookmarks. - if (!importBookmarks) { - // Now apply distribution customized bookmarks. - // This should always run after Places initialization. - try { - this._distributionCustomizer.applyBookmarks(); - this.ensurePlacesDefaultQueriesInitialized(); - } catch (e) { - Cu.reportError(e); - } - } - else { - // An import operation is about to run. - // Don't try to recreate smart bookmarks if autoExportHTML is true or - // smart bookmarks are disabled. - var autoExportHTML = Services.prefs.getBoolPref("browser.bookmarks.autoExportHTML", false); - var smartBookmarksVersion = Services.prefs.getIntPref("browser.places.smartBookmarksVersion", 0); - if (!autoExportHTML && smartBookmarksVersion != -1) - Services.prefs.setIntPref("browser.places.smartBookmarksVersion", 0); - - var bookmarksUrl = null; - if (restoreDefaultBookmarks) { - // User wants to restore bookmarks.html file from default profile folder - bookmarksUrl = "resource:///defaults/profile/bookmarks.html"; - } - else if (yield OS.File.exists(BookmarkHTMLUtils.defaultPath)) { - bookmarksUrl = OS.Path.toFileURI(BookmarkHTMLUtils.defaultPath); - } - - if (bookmarksUrl) { - // Import from bookmarks.html file. - try { - BookmarkHTMLUtils.importFromURL(bookmarksUrl, true).then(null, - function onFailure() { - Cu.reportError( - new Error("Bookmarks.html file could be corrupt.")); - } - ).then( - function onComplete() { - try { - // Now apply distribution customized bookmarks. - // This should always run after Places initialization. - this._distributionCustomizer.applyBookmarks(); - // Ensure that smart bookmarks are created once the operation - // is complete. - this.ensurePlacesDefaultQueriesInitialized(); - } catch (e) { - Cu.reportError(e); - } - }.bind(this) - ); - } catch (e) { - Cu.reportError( - new Error("Bookmarks.html file could be corrupt." + "\n" + - e.message)); - } - } - else { - Cu.reportError(new Error("Unable to find bookmarks.html file.")); - } - - // See #1083: - // "Delete all bookmarks except for backups" in Safe Mode doesn't work - var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - let observer = { - "observe": function() { - delete observer.timer; - // Reset preferences, so we won't try to import again at next run - if (importBookmarksHTML) { - Services.prefs.setBoolPref("browser.places.importBookmarksHTML", false); - } - if (restoreDefaultBookmarks) { - Services.prefs.setBoolPref("browser.bookmarks.restore_default_bookmarks", - false); - } - }, - "timer": timer, - }; - timer.init(observer, 100, Ci.nsITimer.TYPE_ONE_SHOT); - } - - // Initialize bookmark archiving on idle. - // Once a day, either on idle or shutdown, bookmarks are backed up. - if (!this._isIdleObserver) { - this._idleService.addIdleObserver(this, BOOKMARKS_BACKUP_IDLE_TIME); - this._isIdleObserver = true; - } - - }.bind(this)).catch(ex => { - Cu.reportError(ex); - }).then(result => { - // NB: deliberately after the catch so that we always do this, even if - // we threw halfway through initializing in the Task above. - Services.obs.notifyObservers(null, "places-browser-init-complete", ""); - }); - }, - - /** - * Places shut-down tasks - * - back up bookmarks if needed. - * - export bookmarks as HTML, if so configured. - * - finalize components depending on Places. - */ - _onPlacesShutdown: function BG__onPlacesShutdown() { - this._sanitizer.onShutdown(); - PageThumbs.uninit(); - - if (this._isIdleObserver) { - this._idleService.removeIdleObserver(this, BOOKMARKS_BACKUP_IDLE_TIME); - this._isIdleObserver = false; - } - - let waitingForBackupToComplete = true; - if (this._shouldBackupBookmarks()) { - waitingForBackupToComplete = false; - this._backupBookmarks().then( - function onSuccess() { - waitingForBackupToComplete = true; - }, - function onFailure() { - Cu.reportError("Unable to backup bookmarks."); - waitingForBackupToComplete = true; - } - ); - } - - // Backup bookmarks to bookmarks.html to support apps that depend - // on the legacy format. - let waitingForHTMLExportToComplete = true; - // If this fails to get the preference value, we don't export. - if (Services.prefs.getBoolPref("browser.bookmarks.autoExportHTML")) { - // Exceptionally, since this is a non-default setting and HTML format is - // discouraged in favor of the JSON/JSONLZ4 backups, we spin the event - // loop on shutdown, to wait for the export to finish. We cannot safely - // spin the event loop on shutdown until we include a watchdog to prevent - // potential hangs (bug 518683). The asynchronous shutdown operations - // will then be handled by a shutdown service (bug 435058). - waitingForHTMLExportToComplete = false; - BookmarkHTMLUtils.exportToFile(BookmarkHTMLUtils.defaultPath).then( - function onSuccess() { - waitingForHTMLExportToComplete = true; - }, - function onFailure() { - Cu.reportError("Unable to auto export html."); - waitingForHTMLExportToComplete = true; - } - ); - } - - let thread = Services.tm.currentThread; - while (!waitingForBackupToComplete || !waitingForHTMLExportToComplete) { - thread.processNextEvent(true); - } - }, - - /** - * Determine whether to backup bookmarks or not. - * @return true if bookmarks should be backed up, false if not. - */ - _shouldBackupBookmarks: function BG__shouldBackupBookmarks() { - let lastBackupFile = PlacesBackups.getMostRecent(); - - // Should backup bookmarks if there are no backups or the maximum interval between - // backups elapsed. - return (!lastBackupFile || - new Date() - PlacesBackups.getDateForFile(lastBackupFile) > BOOKMARKS_BACKUP_INTERVAL); - }, - - /** - * Backup bookmarks. - */ - _backupBookmarks: function BG__backupBookmarks() { - return Task.spawn(function() { - // Backup bookmarks if there are no backups or the maximum interval between - // backups elapsed. - let maxBackups = BOOKMARKS_BACKUP_MAX_BACKUPS; - try { - maxBackups = Services.prefs.getIntPref("browser.bookmarks.max_backups"); - } - catch(ex) { /* Use default. */ } - - yield PlacesBackups.create(maxBackups); // Don't force creation. - }); - }, - - /** - * Show the notificationBox for a locked places database. - */ - _showPlacesLockedNotificationBox: function BG__showPlacesLockedNotificationBox() { - var applicationName = gBrandBundle.GetStringFromName("brandShortName"); - var placesBundle = Services.strings.createBundle("chrome://browser/locale/places/places.properties"); - var title = placesBundle.GetStringFromName("lockPrompt.title"); - var text = placesBundle.formatStringFromName("lockPrompt.text", [applicationName], 1); - var buttonText = placesBundle.GetStringFromName("lockPromptInfoButton.label"); - var accessKey = placesBundle.GetStringFromName("lockPromptInfoButton.accessKey"); - - var helpTopic = "places-locked"; - var url = Cc["@mozilla.org/toolkit/URLFormatterService;1"]. - getService(Components.interfaces.nsIURLFormatter). - formatURLPref("app.support.baseURL"); - url += helpTopic; - - var win = this.getMostRecentBrowserWindow(); - - var buttons = [ - { - label: buttonText, - accessKey: accessKey, - popup: null, - callback: function(aNotificationBar, aButton) { - win.openUILinkIn(url, "tab"); - } - } - ]; - - var notifyBox = win.gBrowser.getNotificationBox(); - var notification = notifyBox.appendNotification(text, title, null, - notifyBox.PRIORITY_CRITICAL_MEDIUM, - buttons); - notification.persistence = -1; // Until user closes it - }, - - _migrateUI: function BG__migrateUI() { - const UI_VERSION = 20; - const BROWSER_DOCURL = "chrome://browser/content/browser.xul#"; - let currentUIVersion = 0; - try { - currentUIVersion = Services.prefs.getIntPref("browser.migration.version"); - } catch(ex) {} - if (currentUIVersion >= UI_VERSION) - return; - - this._rdf = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService); - this._dataSource = this._rdf.GetDataSource("rdf:local-store"); - this._dirty = false; - - if (currentUIVersion < 2) { - // This code adds the customizable bookmarks button. - let currentsetResource = this._rdf.GetResource("currentset"); - let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar"); - let currentset = this._getPersist(toolbarResource, currentsetResource); - // Need to migrate only if toolbar is customized and the element is not found. - if (currentset && - currentset.indexOf("bookmarks-menu-button-container") == -1) { - currentset += ",bookmarks-menu-button-container"; - this._setPersist(toolbarResource, currentsetResource, currentset); - } - } - -#ifndef MOZ_JXR - // Until JPEG-XR decoder is implemented (UXP #144) - if (currentUIVersion < 19) { - try { - let ihaPref = "image.http.accept"; - let ihaValue = Services.prefs.getCharPref(ihaPref); - if (ihaValue.includes("image/jxr,")) { - Services.prefs.setCharPref(ihaPref, ihaValue.replace("image/jxr,", "")); - } else if (ihaValue.includes("image/jxr")) { - Services.prefs.clearUserPref(ihaPref); - } - } - catch (ex) {} - } -#endif - - if (currentUIVersion < 20) { - // HPKP change of UI preference; reset enforcement level - Services.prefs.clearUserPref("security.cert_pinning.eforcement_level"); - } - - // Update the migration version. - Services.prefs.setIntPref("browser.migration.version", UI_VERSION); - }, - - _hasExistingNotificationPermission: function BG__hasExistingNotificationPermission() { - let enumerator = Services.perms.enumerator; - while (enumerator.hasMoreElements()) { - let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission); - if (permission.type == "desktop-notification") { - return true; - } - } - return false; - }, - - _notifyNotificationsUpgrade: function BG__notifyNotificationsUpgrade() { - if (!this._hasExistingNotificationPermission()) { - return; - } - function clickCallback(subject, topic, data) { - if (topic != "alertclickcallback") - return; - let win = RecentWindow.getMostRecentBrowserWindow(); - win.openUILinkIn(data, "tab"); - } - // Show the application icon for XUL notifications. We assume system-level - // notifications will include their own icon. - let imageURL = this._hasSystemAlertsService() ? "" : - "chrome://branding/content/about-logo.png"; - let title = gBrowserBundle.GetStringFromName("webNotifications.upgradeTitle"); - let text = gBrowserBundle.GetStringFromName("webNotifications.upgradeBody"); - let url = Services.urlFormatter.formatURLPref("browser.push.warning.infoURL"); - - try { - AlertsService.showAlertNotification(imageURL, title, text, - true, url, clickCallback); - } - catch (e) { - Cu.reportError(e); - } - }, - - _openPermissions: function(aPrincipal) { - var win = this.getMostRecentBrowserWindow(); - var url = "about:permissions"; - try { - url = url + "?filter=" + aPrincipal.URI.host; - } - catch (e) {} - win.openUILinkIn(url, "tab"); - }, - - _hasSystemAlertsService: function() { - try { - return !!Cc["@mozilla.org/system-alerts-service;1"].getService( - Ci.nsIAlertsService); - } catch (e) {} - return false; - }, - - _getPersist: function BG__getPersist(aSource, aProperty) { - var target = this._dataSource.GetTarget(aSource, aProperty, true); - if (target instanceof Ci.nsIRDFLiteral) - return target.Value; - return null; - }, - - _setPersist: function BG__setPersist(aSource, aProperty, aTarget) { - this._dirty = true; - try { - var oldTarget = this._dataSource.GetTarget(aSource, aProperty, true); - if (oldTarget) { - if (aTarget) - this._dataSource.Change(aSource, aProperty, oldTarget, this._rdf.GetLiteral(aTarget)); - else - this._dataSource.Unassert(aSource, aProperty, oldTarget); - } - else { - this._dataSource.Assert(aSource, aProperty, this._rdf.GetLiteral(aTarget), true); - } - - // Add the entry to the persisted set for this document if it's not there. - // This code is mostly borrowed from XULDocument::Persist. - let docURL = aSource.ValueUTF8.split("#")[0]; - let docResource = this._rdf.GetResource(docURL); - let persistResource = this._rdf.GetResource("http://home.netscape.com/NC-rdf#persist"); - if (!this._dataSource.HasAssertion(docResource, persistResource, aSource, true)) { - this._dataSource.Assert(docResource, persistResource, aSource, true); - } - } - catch(ex) {} - }, - - // ------------------------------ - // public nsIBrowserGlue members - // ------------------------------ - - sanitize: function BG_sanitize(aParentWindow) { - this._sanitizer.sanitize(aParentWindow); - }, - - ensurePlacesDefaultQueriesInitialized: - function BG_ensurePlacesDefaultQueriesInitialized() { - // This is actual version of the smart bookmarks, must be increased every - // time smart bookmarks change. - // When adding a new smart bookmark below, its newInVersion property must - // be set to the version it has been added in, we will compare its value - // to users' smartBookmarksVersion and add new smart bookmarks without - // recreating old deleted ones. - const SMART_BOOKMARKS_VERSION = 4; - const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark"; - const SMART_BOOKMARKS_PREF = "browser.places.smartBookmarksVersion"; - - // TODO bug 399268: should this be a pref? - const MAX_RESULTS = 10; - - // Get current smart bookmarks version. If not set, create them. - let smartBookmarksCurrentVersion = Services.prefs.getIntPref(SMART_BOOKMARKS_PREF, 0); - - // If version is current or smart bookmarks are disabled, just bail out. - if (smartBookmarksCurrentVersion == -1 || - smartBookmarksCurrentVersion >= SMART_BOOKMARKS_VERSION) { - return; - } - - let batch = { - runBatched: function BG_EPDQI_runBatched() { - let menuIndex = 0; - let toolbarIndex = 0; - let bundle = Services.strings.createBundle("chrome://browser/locale/places/places.properties"); - - let smartBookmarks = { - MostVisited: { - title: bundle.GetStringFromName("mostVisitedTitle"), - uri: NetUtil.newURI("place:sort=" + - Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_DESCENDING + - "&maxResults=" + MAX_RESULTS), - parent: PlacesUtils.toolbarFolderId, - position: toolbarIndex++, - newInVersion: 1 - }, - RecentlyBookmarked: { - title: bundle.GetStringFromName("recentlyBookmarkedTitle"), - uri: NetUtil.newURI("place:folder=BOOKMARKS_MENU" + - "&folder=UNFILED_BOOKMARKS" + - "&folder=TOOLBAR" + - "&queryType=" + - Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS + - "&sort=" + - Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_DESCENDING + - "&maxResults=" + MAX_RESULTS + - "&excludeQueries=1"), - parent: PlacesUtils.bookmarksMenuFolderId, - position: menuIndex++, - newInVersion: 1 - }, - RecentTags: { - title: bundle.GetStringFromName("recentTagsTitle"), - uri: NetUtil.newURI("place:"+ - "type=" + - Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY + - "&sort=" + - Ci.nsINavHistoryQueryOptions.SORT_BY_LASTMODIFIED_DESCENDING + - "&maxResults=" + MAX_RESULTS), - parent: PlacesUtils.bookmarksMenuFolderId, - position: menuIndex++, - newInVersion: 1 - } - }; - - // Set current itemId, parent and position if Smart Bookmark exists, - // we will use these informations to create the new version at the same - // position. - let smartBookmarkItemIds = PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO); - smartBookmarkItemIds.forEach(function (itemId) { - let queryId = PlacesUtils.annotations.getItemAnnotation(itemId, SMART_BOOKMARKS_ANNO); - if (queryId in smartBookmarks) { - let smartBookmark = smartBookmarks[queryId]; - smartBookmark.itemId = itemId; - smartBookmark.parent = PlacesUtils.bookmarks.getFolderIdForItem(itemId); - smartBookmark.position = PlacesUtils.bookmarks.getItemIndex(itemId); - } - else { - // We don't remove old Smart Bookmarks because user could still - // find them useful, or could have personalized them. - // Instead we remove the Smart Bookmark annotation. - PlacesUtils.annotations.removeItemAnnotation(itemId, SMART_BOOKMARKS_ANNO); - } - }); - - for (let queryId in smartBookmarks) { - let smartBookmark = smartBookmarks[queryId]; - - // We update or create only changed or new smart bookmarks. - // Also we respect user choices, so we won't try to create a smart - // bookmark if it has been removed. - if (smartBookmarksCurrentVersion > 0 && - smartBookmark.newInVersion <= smartBookmarksCurrentVersion && - !smartBookmark.itemId) - continue; - - // Remove old version of the smart bookmark if it exists, since it - // will be replaced in place. - if (smartBookmark.itemId) { - PlacesUtils.bookmarks.removeItem(smartBookmark.itemId); - } - - // Create the new smart bookmark and store its updated itemId. - smartBookmark.itemId = - PlacesUtils.bookmarks.insertBookmark(smartBookmark.parent, - smartBookmark.uri, - smartBookmark.position, - smartBookmark.title); - PlacesUtils.annotations.setItemAnnotation(smartBookmark.itemId, - SMART_BOOKMARKS_ANNO, - queryId, 0, - PlacesUtils.annotations.EXPIRE_NEVER); - } - - // If we are creating all Smart Bookmarks from ground up, add a - // separator below them in the bookmarks menu. - if (smartBookmarksCurrentVersion == 0 && - smartBookmarkItemIds.length == 0) { - let id = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, - menuIndex); - // Don't add a separator if the menu was empty or there is one already. - if (id != -1 && - PlacesUtils.bookmarks.getItemType(id) != PlacesUtils.bookmarks.TYPE_SEPARATOR) { - PlacesUtils.bookmarks.insertSeparator(PlacesUtils.bookmarksMenuFolderId, - menuIndex); - } - } - } - }; - - try { - PlacesUtils.bookmarks.runInBatchMode(batch, null); - } - catch(ex) { - Components.utils.reportError(ex); - } - finally { - Services.prefs.setIntPref(SMART_BOOKMARKS_PREF, SMART_BOOKMARKS_VERSION); - Services.prefs.savePrefFile(null); - } - }, - - // this returns the most recent non-popup browser window - getMostRecentBrowserWindow: function BG_getMostRecentBrowserWindow() { - return RecentWindow.getMostRecentBrowserWindow(); - }, - -#ifdef MOZ_SERVICES_SYNC - /** - * Called as an observer when Sync's "display URI" notification is fired. - * - * We open the received URI in a background tab. - * - * Eventually, this will likely be replaced by a more robust tab syncing - * feature. This functionality is considered somewhat evil by UX because it - * opens a new tab automatically without any prompting. However, it is a - * lesser evil than sending a tab to a specific device (from e.g. Fennec) - * and having nothing happen on the receiving end. - */ - _onDisplaySyncURI: function _onDisplaySyncURI(data) { - try { - let tabbrowser = RecentWindow.getMostRecentBrowserWindow({private: false}).gBrowser; - - // The payload is wrapped weirdly because of how Sync does notifications. - tabbrowser.addTab(data.wrappedJSObject.object.uri); - } catch (ex) { - Cu.reportError("Error displaying tab received by Sync: " + ex); - } - }, -#endif - - // for XPCOM - classID: Components.ID("{eab9012e-5f74-4cbc-b2b5-a590235513cc}"), - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, - Ci.nsISupportsWeakReference, - Ci.nsIBrowserGlue]), - - // redefine the default factory for XPCOMUtils - _xpcom_factory: BrowserGlueServiceFactory, -} - -function ContentPermissionPrompt() {} - -ContentPermissionPrompt.prototype = { - classID: Components.ID("{d8903bf6-68d5-4e97-bcd1-e4d3012f721a}"), - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]), - - _getChromeWindow: function CPP_getChromeWindow(aWindow) { - var chromeWin = aWindow - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShellTreeItem) - .rootTreeItem - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindow) - .QueryInterface(Ci.nsIDOMChromeWindow); - return chromeWin; - }, - - _getBrowserForRequest: function (aRequest) { - let requestingWindow = aRequest.window.top; - // find the requesting browser or iframe - let browser = requestingWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShell) - .chromeEventHandler; - return browser; - }, - - /** - * Show a permission prompt. - * - * @param aRequest The permission request. - * @param aMessage The message to display on the prompt. - * @param aPermission The type of permission to prompt. - * @param aActions An array of actions of the form: - * [main action, secondary actions, ...] - * Actions are of the form { stringId, action, expireType, callback } - * Permission is granted if action is null or ALLOW_ACTION. - * @param aNotificationId The id of the PopupNotification. - * @param aAnchorId The id for the PopupNotification anchor. - * @param aOptions Options for the PopupNotification - */ - _showPrompt: function CPP_showPrompt(aRequest, aMessage, aPermission, aActions, - aNotificationId, aAnchorId, aOptions) { - function onFullScreen() { - popup.remove(); - } - - var requestingWindow = aRequest.window.top; - var chromeWin = this._getChromeWindow(requestingWindow).wrappedJSObject; - var browser = chromeWin.gBrowser.getBrowserForDocument(requestingWindow.document); - if (!browser) { - // find the requesting browser or iframe - browser = requestingWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShell) - .chromeEventHandler; - } - var requestPrincipal = aRequest.principal; - - // Transform the prompt actions into PopupNotification actions. - var popupNotificationActions = []; - for (var i = 0; i < aActions.length; i++) { - let promptAction = aActions[i]; - - // Don't offer action in PB mode if the action remembers permission for more than a session. - if (PrivateBrowsingUtils.isWindowPrivate(chromeWin) && - promptAction.expireType != Ci.nsIPermissionManager.EXPIRE_SESSION && - promptAction.action) { - continue; - } - - var action = { - label: gBrowserBundle.GetStringFromName(promptAction.stringId), - accessKey: gBrowserBundle.GetStringFromName(promptAction.stringId + ".accesskey"), - callback: function() { - if (promptAction.callback) { - promptAction.callback(); - } - - // Remember permissions. - if (promptAction.action) { - Services.perms.addFromPrincipal(requestPrincipal, aPermission, - promptAction.action, promptAction.expireType); - } - - // Grant permission if action is null or ALLOW_ACTION. - if (!promptAction.action || promptAction.action == Ci.nsIPermissionManager.ALLOW_ACTION) { - aRequest.allow(); - } else { - aRequest.cancel(); - } - }, - }; - - popupNotificationActions.push(action); - } - - var mainAction = popupNotificationActions.length ? - popupNotificationActions[0] : null; - var secondaryActions = popupNotificationActions.splice(1); - - if (aRequest.type == "pointerLock") { - // If there's no mainAction, this is the autoAllow warning prompt. - let autoAllow = !mainAction; - - if (!aOptions) - aOptions = {}; - - aOptions.removeOnDismissal = autoAllow; - aOptions.eventCallback = type => { - if (type == "removed") { - browser.removeEventListener("mozfullscreenchange", onFullScreen, true); - if (autoAllow) { - aRequest.allow(); - } - } - } - - } - - var popup = chromeWin.PopupNotifications.show(browser, aNotificationId, aMessage, aAnchorId, - mainAction, secondaryActions, aOptions); - if (aRequest.type == "pointerLock") { - // pointerLock is automatically allowed in fullscreen mode (and revoked - // upon exit), so if the page enters fullscreen mode after requesting - // pointerLock (but before the user has granted permission), we should - // remove the now-impotent notification. - browser.addEventListener("mozfullscreenchange", onFullScreen, true); - } - }, - - _promptGeo : function(aRequest) { - var requestingURI = aRequest.principal.URI; - - var message; - - // Share location action. - var actions = [{ - stringId: "geolocation.shareLocation", - action: null, - expireType: null, - callback: function() { - // Telemetry stub (left here for safety and compatibility reasons) - }, - }]; - - if (requestingURI.schemeIs("file")) { - message = gBrowserBundle.formatStringFromName("geolocation.shareWithFile", - [requestingURI.path], 1); - } else { - message = gBrowserBundle.formatStringFromName("geolocation.shareWithSite", - [requestingURI.host], 1); - // Always share location action. - actions.push({ - stringId: "geolocation.alwaysShareLocation", - action: Ci.nsIPermissionManager.ALLOW_ACTION, - expireType: null, - callback: function() { - // Telemetry stub (left here for safety and compatibility reasons) - }, - }); - - // Never share location action. - actions.push({ - stringId: "geolocation.neverShareLocation", - action: Ci.nsIPermissionManager.DENY_ACTION, - expireType: null, - callback: function() { - // Telemetry stub (left here for safety and compatibility reasons) - }, - }); - } - - var options = { - learnMoreURL: Services.urlFormatter.formatURLPref("browser.geolocation.warning.infoURL"), - }; - - this._showPrompt(aRequest, message, "geo", actions, "geolocation", - "geo-notification-icon", options); - }, - - _promptWebNotifications : function(aRequest) { - var requestingURI = aRequest.principal.URI; - - var message = gBrowserBundle.formatStringFromName("webNotifications.showFromSite", - [requestingURI.host], 1); - - var actions; - - var browser = this._getBrowserForRequest(aRequest); - // Only show "allow for session" in PB mode, we don't - // support "allow for session" in non-PB mode. - if (PrivateBrowsingUtils.isBrowserPrivate(browser)) { - actions = [ - { - stringId: "webNotifications.showForSession", - action: Ci.nsIPermissionManager.ALLOW_ACTION, - expireType: Ci.nsIPermissionManager.EXPIRE_SESSION, - callback: function() {}, - }, - ]; - } else { - actions = [ - { - stringId: "webNotifications.showForSession", - action: Ci.nsIPermissionManager.ALLOW_ACTION, - expireType: Ci.nsIPermissionManager.EXPIRE_SESSION, - callback: function() {}, - }, - { - stringId: "webNotifications.alwaysShow", - action: Ci.nsIPermissionManager.ALLOW_ACTION, - expireType: null, - callback: function() {}, - }, - { - stringId: "webNotifications.neverShow", - action: Ci.nsIPermissionManager.DENY_ACTION, - expireType: null, - callback: function() {}, - }, - ]; - } - var options = { - learnMoreURL: Services.urlFormatter.formatURLPref("browser.push.warning.infoURL"), - }; - - this._showPrompt(aRequest, message, "desktop-notification", actions, - "web-notifications", - "web-notifications-notification-icon", options); - }, - - _promptPointerLock: function CPP_promtPointerLock(aRequest, autoAllow) { - let requestingURI = aRequest.principal.URI; - - let originString = requestingURI.schemeIs("file") ? requestingURI.path : requestingURI.host; - let message = gBrowserBundle.formatStringFromName(autoAllow ? - "pointerLock.autoLock.title2" : "pointerLock.title2", - [originString], 1); - // If this is an autoAllow info prompt, offer no actions. - // _showPrompt() will allow the request when it's dismissed. - let actions = []; - if (!autoAllow) { - actions = [ - { - stringId: "pointerLock.allow2", - action: null, - expireType: null, - callback: function() {}, - }, - { - stringId: "pointerLock.alwaysAllow", - action: Ci.nsIPermissionManager.ALLOW_ACTION, - expireType: null, - callback: function() {}, - }, - { - stringId: "pointerLock.neverAllow", - action: Ci.nsIPermissionManager.DENY_ACTION, - expireType: null, - callback: function() {}, - }, - ]; - } - - this._showPrompt(aRequest, message, "pointerLock", actions, "pointerLock", - "pointerLock-notification-icon", null); - }, - - prompt: function CPP_prompt(request) { - // Only allow exactly one permission rquest here. - let types = request.types.QueryInterface(Ci.nsIArray); - if (types.length != 1) { - request.cancel(); - return; - } - let perm = types.queryElementAt(0, Ci.nsIContentPermissionType); - - const kFeatureKeys = { "geolocation" : "geo", - "desktop-notification" : "desktop-notification", - "pointerLock" : "pointerLock", - }; - - // Make sure that we support the request. - if (!(perm.type in kFeatureKeys)) { - return; - } - - var requestingPrincipal = request.principal; - var requestingURI = requestingPrincipal.URI; - - // Ignore requests from non-nsIStandardURLs - if (!(requestingURI instanceof Ci.nsIStandardURL)) - return; - - var autoAllow = false; - var permissionKey = kFeatureKeys[perm.type]; - var result = Services.perms.testExactPermissionFromPrincipal(requestingPrincipal, permissionKey); - - if (result == Ci.nsIPermissionManager.DENY_ACTION) { - request.cancel(); - return; - } - - if (result == Ci.nsIPermissionManager.ALLOW_ACTION) { - autoAllow = true; - // For pointerLock, we still want to show a warning prompt. - if (request.type != "pointerLock") { - request.allow(); - return; - } - } - - // Show the prompt. - switch (perm.type) { - case "geolocation": - this._promptGeo(request); - break; - case "desktop-notification": - this._promptWebNotifications(request); - break; - case "pointerLock": - this._promptPointerLock(request, autoAllow); - break; - } - }, - -}; - -var components = [BrowserGlue, ContentPermissionPrompt]; -this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components); diff --git a/components/nsIBrowserGlue.idl b/components/nsIBrowserGlue.idl deleted file mode 100644 index 1bb82a9..0000000 --- a/components/nsIBrowserGlue.idl +++ /dev/null @@ -1,47 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" - -interface nsIDOMWindow; - -/** - * nsIBrowserGlue is a dirty and rather fluid interface to host shared utility - * methods used by browser UI code, but which are not local to a browser window. - * The component implementing this interface is meant to be a singleton - * (service) and should progressively replace some of the shared "glue" code - * scattered in browser/base/content (e.g. bits of utilOverlay.js, - * contentAreaUtils.js, globalOverlay.js, browser.js), avoiding dynamic - * inclusion and initialization of a ton of JS code for *each* window. - * Dued to its nature and origin, this interface won't probably be the most - * elegant or stable in the mozilla codebase, but its aim is rather pragmatic: - * 1) reducing the performance overhead which affects browser window load; - * 2) allow global hooks (e.g. startup and shutdown observers) which survive - * browser windows to accomplish browser-related activities, such as shutdown - * sanitization (see bug #284086) - * - */ - -[scriptable, uuid(781df699-17dc-4237-b3d7-876ddb7085e3)] -interface nsIBrowserGlue : nsISupports -{ - /** - * Deletes privacy sensitive data according to user preferences - * - * @param aParentWindow an optionally null window which is the parent of the - * sanitization dialog - * - */ - void sanitize(in nsIDOMWindow aParentWindow); - - /** - * Add Smart Bookmarks special queries to bookmarks menu and toolbar folder. - */ - void ensurePlacesDefaultQueriesInitialized(); - - /** - * Gets the most recent window that's a browser (but not a popup) - */ - nsIDOMWindow getMostRecentBrowserWindow(); -}; diff --git a/components/nsIBrowserHandler.idl b/components/nsIBrowserHandler.idl deleted file mode 100644 index 74292f9..0000000 --- a/components/nsIBrowserHandler.idl +++ /dev/null @@ -1,20 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" - -interface nsICommandLine; - -[scriptable, uuid(8D3F5A9D-118D-4548-A137-CF7718679069)] -interface nsIBrowserHandler : nsISupports -{ - attribute AUTF8String startPage; - attribute AUTF8String defaultArgs; - - /** - * Extract the width and height specified on the command line, if present. - * @return A feature string with a prepended comma, e.g. ",width=500,height=400" - */ - AUTF8String getFeatures(in nsICommandLine aCmdLine); -}; diff --git a/components/pageinfo/feeds.js b/components/pageinfo/feeds.js deleted file mode 100644 index 468d8c1..0000000 --- a/components/pageinfo/feeds.js +++ /dev/null @@ -1,59 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -function initFeedTab() -{ - const feedTypes = { - "application/rss+xml": gBundle.getString("feedRss"), - "application/atom+xml": gBundle.getString("feedAtom"), - "text/xml": gBundle.getString("feedXML"), - "application/xml": gBundle.getString("feedXML"), - "application/rdf+xml": gBundle.getString("feedXML") - }; - - // get the feeds - var linkNodes = gDocument.getElementsByTagName("link"); - var length = linkNodes.length; - for (var i = 0; i < length; i++) { - var link = linkNodes[i]; - if (!link.href) - continue; - - var rel = link.rel && link.rel.toLowerCase(); - var rels = {}; - if (rel) { - for each (let relVal in rel.split(/\s+/)) - rels[relVal] = true; - } - - if (rels.feed || (link.type && rels.alternate && !rels.stylesheet)) { - var type = isValidFeed(link, gDocument.nodePrincipal, "feed" in rels); - if (type) { - type = feedTypes[type] || feedTypes["application/rss+xml"]; - addRow(link.title, type, link.href); - } - } - } - - var feedListbox = document.getElementById("feedListbox"); - document.getElementById("feedTab").hidden = feedListbox.getRowCount() == 0; -} - -function onSubscribeFeed() -{ - var listbox = document.getElementById("feedListbox"); - openUILinkIn(listbox.selectedItem.getAttribute("feedURL"), "current", - { ignoreAlt: true }); -} - -function addRow(name, type, url) -{ - var item = document.createElement("richlistitem"); - item.setAttribute("feed", "true"); - item.setAttribute("name", name); - item.setAttribute("type", type); - item.setAttribute("feedURL", url); - document.getElementById("feedListbox").appendChild(item); -} diff --git a/components/pageinfo/feeds.xml b/components/pageinfo/feeds.xml deleted file mode 100644 index 782c05a..0000000 --- a/components/pageinfo/feeds.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0"?> -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<!DOCTYPE bindings [ - <!ENTITY % pageInfoDTD SYSTEM "chrome://browser/locale/pageInfo.dtd"> - %pageInfoDTD; -]> - -<bindings id="feedBindings" - xmlns="http://www.mozilla.org/xbl" - xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:xbl="http://www.mozilla.org/xbl"> - - <binding id="feed" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem"> - <content> - <xul:vbox flex="1"> - <xul:hbox flex="1"> - <xul:textbox flex="1" readonly="true" xbl:inherits="value=name" - class="feedTitle"/> - <xul:label xbl:inherits="value=type"/> - </xul:hbox> - <xul:vbox> - <xul:vbox align="start"> - <xul:hbox> - <xul:label xbl:inherits="value=feedURL,tooltiptext=feedURL" class="text-link" flex="1" - onclick="openUILink(this.value, event);" crop="end"/> - </xul:hbox> - </xul:vbox> - </xul:vbox> - <xul:hbox flex="1" class="feed-subscribe"> - <xul:spacer flex="1"/> - <xul:button label="&feedSubscribe;" accesskey="&feedSubscribe.accesskey;" - oncommand="onSubscribeFeed()"/> - </xul:hbox> - </xul:vbox> - </content> - </binding> -</bindings> diff --git a/components/pageinfo/jar.mn b/components/pageinfo/jar.mn deleted file mode 100644 index 229f991..0000000 --- a/components/pageinfo/jar.mn +++ /dev/null @@ -1,13 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -browser.jar: -* content/browser/pageinfo/pageInfo.xul - content/browser/pageinfo/pageInfo.js - content/browser/pageinfo/pageInfo.css - content/browser/pageinfo/pageInfo.xml - content/browser/pageinfo/feeds.js - content/browser/pageinfo/feeds.xml - content/browser/pageinfo/permissions.js - content/browser/pageinfo/security.js
\ No newline at end of file diff --git a/components/pageinfo/moz.build b/components/pageinfo/moz.build deleted file mode 100644 index 2d64d50..0000000 --- a/components/pageinfo/moz.build +++ /dev/null @@ -1,8 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -JAR_MANIFESTS += ['jar.mn'] - diff --git a/components/pageinfo/pageInfo.css b/components/pageinfo/pageInfo.css deleted file mode 100644 index 622b56b..0000000 --- a/components/pageinfo/pageInfo.css +++ /dev/null @@ -1,26 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - - -#viewGroup > radio { - -moz-binding: url("chrome://browser/content/pageinfo/pageInfo.xml#viewbutton"); -} - -richlistitem[feed] { - -moz-binding: url("chrome://browser/content/pageinfo/feeds.xml#feed"); -} - -richlistitem[feed]:not([selected="true"]) .feed-subscribe { - display: none; -} - -groupbox[closed="true"] > .groupbox-body { - visibility: collapse; -} - -#thepreviewimage { - display: block; -/* This following entry can be removed when Bug 522850 is fixed. */ - min-width: 1px; -} diff --git a/components/pageinfo/pageInfo.js b/components/pageinfo/pageInfo.js deleted file mode 100644 index 600174a..0000000 --- a/components/pageinfo/pageInfo.js +++ /dev/null @@ -1,1286 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var Cu = Components.utils; -Cu.import("resource://gre/modules/LoadContextInfo.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -//******** define a js object to implement nsITreeView -function pageInfoTreeView(treeid, copycol) -{ - // copycol is the index number for the column that we want to add to - // the copy-n-paste buffer when the user hits accel-c - this.treeid = treeid; - this.copycol = copycol; - this.rows = 0; - this.tree = null; - this.data = [ ]; - this.selection = null; - this.sortcol = -1; - this.sortdir = false; -} - -pageInfoTreeView.prototype = { - set rowCount(c) { throw "rowCount is a readonly property"; }, - get rowCount() { return this.rows; }, - - setTree: function(tree) - { - this.tree = tree; - }, - - getCellText: function(row, column) - { - // row can be null, but js arrays are 0-indexed. - // colidx cannot be null, but can be larger than the number - // of columns in the array. In this case it's the fault of - // whoever typoed while calling this function. - return this.data[row][column.index] || ""; - }, - - setCellValue: function(row, column, value) - { - }, - - setCellText: function(row, column, value) - { - this.data[row][column.index] = value; - }, - - addRow: function(row) - { - this.rows = this.data.push(row); - this.rowCountChanged(this.rows - 1, 1); - if (this.selection.count == 0 && this.rowCount && !gImageElement) - this.selection.select(0); - }, - - rowCountChanged: function(index, count) - { - this.tree.rowCountChanged(index, count); - }, - - invalidate: function() - { - this.tree.invalidate(); - }, - - clear: function() - { - if (this.tree) - this.tree.rowCountChanged(0, -this.rows); - this.rows = 0; - this.data = [ ]; - }, - - handleCopy: function(row) - { - return (row < 0 || this.copycol < 0) ? "" : (this.data[row][this.copycol] || ""); - }, - - performActionOnRow: function(action, row) - { - if (action == "copy") { - var data = this.handleCopy(row) - this.tree.treeBody.parentNode.setAttribute("copybuffer", data); - } - }, - - onPageMediaSort : function(columnname) - { - var tree = document.getElementById(this.treeid); - var treecol = tree.columns.getNamedColumn(columnname); - - this.sortdir = - gTreeUtils.sort( - tree, - this, - this.data, - treecol.index, - function textComparator(a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); }, - this.sortcol, - this.sortdir - ); - - this.sortcol = treecol.index; - }, - - getRowProperties: function(row) { return ""; }, - getCellProperties: function(row, column) { return ""; }, - getColumnProperties: function(column) { return ""; }, - isContainer: function(index) { return false; }, - isContainerOpen: function(index) { return false; }, - isSeparator: function(index) { return false; }, - isSorted: function() { }, - canDrop: function(index, orientation) { return false; }, - drop: function(row, orientation) { return false; }, - getParentIndex: function(index) { return 0; }, - hasNextSibling: function(index, after) { return false; }, - getLevel: function(index) { return 0; }, - getImageSrc: function(row, column) { }, - getProgressMode: function(row, column) { }, - getCellValue: function(row, column) { }, - toggleOpenState: function(index) { }, - cycleHeader: function(col) { }, - selectionChanged: function() { }, - cycleCell: function(row, column) { }, - isEditable: function(row, column) { return false; }, - isSelectable: function(row, column) { return false; }, - performAction: function(action) { }, - performActionOnCell: function(action, row, column) { } -}; - -// mmm, yummy. global variables. -var gWindow = null; -var gDocument = null; -var gImageElement = null; - -// column number to help using the data array -const COL_IMAGE_ADDRESS = 0; -const COL_IMAGE_TYPE = 1; -const COL_IMAGE_SIZE = 2; -const COL_IMAGE_ALT = 3; -const COL_IMAGE_COUNT = 4; -const COL_IMAGE_NODE = 5; -const COL_IMAGE_BG = 6; - -// column number to copy from, second argument to pageInfoTreeView's constructor -const COPYCOL_NONE = -1; -const COPYCOL_META_CONTENT = 1; -const COPYCOL_IMAGE = COL_IMAGE_ADDRESS; - -// one nsITreeView for each tree in the window -var gMetaView = new pageInfoTreeView('metatree', COPYCOL_META_CONTENT); -var gImageView = new pageInfoTreeView('imagetree', COPYCOL_IMAGE); - -gImageView.getCellProperties = function(row, col) { - var data = gImageView.data[row]; - var item = gImageView.data[row][COL_IMAGE_NODE]; - var props = ""; - if (!checkProtocol(data) || - item instanceof HTMLEmbedElement || - (item instanceof HTMLObjectElement && !item.type.startsWith("image/"))) - props += "broken"; - - if (col.element.id == "image-address") - props += " ltr"; - - return props; -}; - -gImageView.getCellText = function(row, column) { - var value = this.data[row][column.index]; - if (column.index == COL_IMAGE_SIZE) { - if (value == -1) { - return gStrings.unknown; - } else { - var kbSize = Number(Math.round(value / 1024 * 100) / 100); - return gBundle.getFormattedString("mediaFileSize", [kbSize]); - } - } - return value || ""; -}; - -gImageView.onPageMediaSort = function(columnname) { - var tree = document.getElementById(this.treeid); - var treecol = tree.columns.getNamedColumn(columnname); - - var comparator; - if (treecol.index == COL_IMAGE_SIZE || treecol.index == COL_IMAGE_COUNT) { - comparator = function numComparator(a, b) { return a - b; }; - } else { - comparator = function textComparator(a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); }; - } - - this.sortdir = - gTreeUtils.sort( - tree, - this, - this.data, - treecol.index, - comparator, - this.sortcol, - this.sortdir - ); - - this.sortcol = treecol.index; -}; - -var gImageHash = { }; - -// localized strings (will be filled in when the document is loaded) -// this isn't all of them, these are just the ones that would otherwise have been loaded inside a loop -var gStrings = { }; -var gBundle; - -const PERMISSION_CONTRACTID = "@mozilla.org/permissionmanager;1"; -const PREFERENCES_CONTRACTID = "@mozilla.org/preferences-service;1"; -const ATOM_CONTRACTID = "@mozilla.org/atom-service;1"; - -// a number of services I'll need later -// the cache services -const nsICacheStorageService = Components.interfaces.nsICacheStorageService; -const nsICacheStorage = Components.interfaces.nsICacheStorage; -const cacheService = Components.classes["@mozilla.org/netwerk/cache-storage-service;1"].getService(nsICacheStorageService); - -var loadContextInfo = LoadContextInfo.fromLoadContext( - window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsIWebNavigation) - .QueryInterface(Components.interfaces.nsILoadContext), false); -var diskStorage = cacheService.diskCacheStorage(loadContextInfo, false); - -const nsICookiePermission = Components.interfaces.nsICookiePermission; -const nsIPermissionManager = Components.interfaces.nsIPermissionManager; - -const nsICertificateDialogs = Components.interfaces.nsICertificateDialogs; -const CERTIFICATEDIALOGS_CONTRACTID = "@mozilla.org/nsCertificateDialogs;1" - -// clipboard helper -function getClipboardHelper() { - try { - return Components.classes["@mozilla.org/widget/clipboardhelper;1"].getService(Components.interfaces.nsIClipboardHelper); - } catch(e) { - // do nothing, later code will handle the error - } -} -const gClipboardHelper = getClipboardHelper(); - -// Interface for image loading content -const nsIImageLoadingContent = Components.interfaces.nsIImageLoadingContent; - -// namespaces, don't need all of these yet... -const XLinkNS = "http://www.w3.org/1999/xlink"; -const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; -const XMLNS = "http://www.w3.org/XML/1998/namespace"; -const XHTMLNS = "http://www.w3.org/1999/xhtml"; -const XHTML2NS = "http://www.w3.org/2002/06/xhtml2" - -const XHTMLNSre = "^http\:\/\/www\.w3\.org\/1999\/xhtml$"; -const XHTML2NSre = "^http\:\/\/www\.w3\.org\/2002\/06\/xhtml2$"; -const XHTMLre = RegExp(XHTMLNSre + "|" + XHTML2NSre, ""); - -/* Overlays register functions here. - * These arrays are used to hold callbacks that Page Info will call at - * various stages. Use them by simply appending a function to them. - * For example, add a function to onLoadRegistry by invoking - * "onLoadRegistry.push(XXXLoadFunc);" - * The XXXLoadFunc should be unique to the overlay module, and will be - * invoked as "XXXLoadFunc();" - */ - -// These functions are called to build the data displayed in the Page -// Info window. The global variables gDocument and gWindow are set. -var onLoadRegistry = [ ]; - -// These functions are called to remove old data still displayed in -// the window when the document whose information is displayed -// changes. For example, at this time, the list of images of the Media -// tab is cleared. -var onResetRegistry = [ ]; - -// These are called once for each subframe of the target document and -// the target document itself. The frame is passed as an argument. -var onProcessFrame = [ ]; - -// These functions are called once for each element (in all subframes, if any) -// in the target document. The element is passed as an argument. -var onProcessElement = [ ]; - -// These functions are called once when all the elements in all of the target -// document (and all of its subframes, if any) have been processed -var onFinished = [ ]; - -// These functions are called once when the Page Info window is closed. -var onUnloadRegistry = [ ]; - -// These functions are called once when an image preview is shown. -var onImagePreviewShown = [ ]; - -/* Called when PageInfo window is loaded. Arguments are: - * window.arguments[0] - (optional) an object consisting of - * - doc: (optional) document to use for source. if not provided, - * the calling window's document will be used - * - initialTab: (optional) id of the inital tab to display - */ -function onLoadPageInfo() -{ - gBundle = document.getElementById("pageinfobundle"); - gStrings.unknown = gBundle.getString("unknown"); - gStrings.notSet = gBundle.getString("notset"); - gStrings.mediaImg = gBundle.getString("mediaImg"); - gStrings.mediaBGImg = gBundle.getString("mediaBGImg"); - gStrings.mediaBorderImg = gBundle.getString("mediaBorderImg"); - gStrings.mediaListImg = gBundle.getString("mediaListImg"); - gStrings.mediaCursor = gBundle.getString("mediaCursor"); - gStrings.mediaObject = gBundle.getString("mediaObject"); - gStrings.mediaEmbed = gBundle.getString("mediaEmbed"); - gStrings.mediaLink = gBundle.getString("mediaLink"); - gStrings.mediaInput = gBundle.getString("mediaInput"); - gStrings.mediaVideo = gBundle.getString("mediaVideo"); - gStrings.mediaAudio = gBundle.getString("mediaAudio"); - - var args = "arguments" in window && - window.arguments.length >= 1 && - window.arguments[0]; - - if (!args || !args.doc) { - gWindow = window.opener.content; - gDocument = gWindow.document; - } - - // init media view - var imageTree = document.getElementById("imagetree"); - imageTree.view = gImageView; - - /* Select the requested tab, if the name is specified */ - loadTab(args); - Components.classes["@mozilla.org/observer-service;1"] - .getService(Components.interfaces.nsIObserverService) - .notifyObservers(window, "page-info-dialog-loaded", null); - - // Make sure the page info window gets focus even if a doorhanger might - // otherwise (async) steal it. - window.focus(); -} - -function loadPageInfo() -{ - var titleFormat = gWindow != gWindow.top ? "pageInfo.frame.title" - : "pageInfo.page.title"; - document.title = gBundle.getFormattedString(titleFormat, [gDocument.location]); - - document.getElementById("main-window").setAttribute("relatedUrl", gDocument.location); - - // do the easy stuff first - makeGeneralTab(); - - // and then the hard stuff - makeTabs(gDocument, gWindow); - - initFeedTab(); - onLoadPermission(gDocument.nodePrincipal); - - /* Call registered overlay init functions */ - onLoadRegistry.forEach(function(func) { func(); }); -} - -function resetPageInfo(args) -{ - /* Reset Meta tags part */ - gMetaView.clear(); - - /* Reset Media tab */ - var mediaTab = document.getElementById("mediaTab"); - if (!mediaTab.hidden) { - Components.classes["@mozilla.org/observer-service;1"] - .getService(Components.interfaces.nsIObserverService) - .removeObserver(imagePermissionObserver, "perm-changed"); - mediaTab.hidden = true; - } - gImageView.clear(); - gImageHash = {}; - - /* Reset Feeds Tab */ - var feedListbox = document.getElementById("feedListbox"); - while (feedListbox.firstChild) - feedListbox.removeChild(feedListbox.firstChild); - - /* Call registered overlay reset functions */ - onResetRegistry.forEach(function(func) { func(); }); - - /* Rebuild the data */ - loadTab(args); -} - -function onUnloadPageInfo() -{ - // Remove the observer, only if there is at least 1 image. - if (!document.getElementById("mediaTab").hidden) { - Components.classes["@mozilla.org/observer-service;1"] - .getService(Components.interfaces.nsIObserverService) - .removeObserver(imagePermissionObserver, "perm-changed"); - } - - /* Call registered overlay unload functions */ - onUnloadRegistry.forEach(function(func) { func(); }); -} - -function doHelpButton() -{ - const helpTopics = { - "generalPanel": "pageinfo_general", - "mediaPanel": "pageinfo_media", - "feedPanel": "pageinfo_feed", - "permPanel": "pageinfo_permissions", - "securityPanel": "pageinfo_security" - }; - - var deck = document.getElementById("mainDeck"); - var helpdoc = helpTopics[deck.selectedPanel.id] || "pageinfo_general"; - openHelpLink(helpdoc); -} - -function showTab(id) -{ - var deck = document.getElementById("mainDeck"); - var pagel = document.getElementById(id + "Panel"); - deck.selectedPanel = pagel; -} - -function loadTab(args) -{ - if (args && args.doc) { - gDocument = args.doc; - gWindow = gDocument.defaultView; - } - - gImageElement = args && args.imageElement; - - /* Load the page info */ - loadPageInfo(); - - var initialTab = (args && args.initialTab) || "generalTab"; - var radioGroup = document.getElementById("viewGroup"); - initialTab = document.getElementById(initialTab) || document.getElementById("generalTab"); - radioGroup.selectedItem = initialTab; - radioGroup.selectedItem.doCommand(); - radioGroup.focus(); -} - -function onClickMore() -{ - var radioGrp = document.getElementById("viewGroup"); - var radioElt = document.getElementById("securityTab"); - radioGrp.selectedItem = radioElt; - showTab('security'); -} - -function toggleGroupbox(id) -{ - var elt = document.getElementById(id); - if (elt.hasAttribute("closed")) { - elt.removeAttribute("closed"); - if (elt.flexWhenOpened) - elt.flex = elt.flexWhenOpened; - } - else { - elt.setAttribute("closed", "true"); - if (elt.flex) { - elt.flexWhenOpened = elt.flex; - elt.flex = 0; - } - } -} - -function openCacheEntry(key, cb) -{ - var checkCacheListener = { - onCacheEntryCheck: function(entry, appCache) { - return Components.interfaces.nsICacheEntryOpenCallback.ENTRY_WANTED; - }, - onCacheEntryAvailable: function(entry, isNew, appCache, status) { - cb(entry); - }, - get mainThreadOnly() { return true; } - }; - diskStorage.asyncOpenURI(Services.io.newURI(key, null, null), "", nsICacheStorage.OPEN_READONLY, checkCacheListener); -} - -function makeGeneralTab() -{ - var title = (gDocument.title) ? gBundle.getFormattedString("pageTitle", [gDocument.title]) : gBundle.getString("noPageTitle"); - document.getElementById("titletext").value = title; - - var url = gDocument.location.toString(); - setItemValue("urltext", url); - - var referrer = ("referrer" in gDocument && gDocument.referrer); - setItemValue("refertext", referrer); - - var mode = ("compatMode" in gDocument && gDocument.compatMode == "BackCompat") ? "generalQuirksMode" : "generalStrictMode"; - document.getElementById("modetext").value = gBundle.getString(mode); - - // find out the mime type - var mimeType = gDocument.contentType; - setItemValue("typetext", mimeType); - - // get the document characterset - var encoding = gDocument.characterSet; - document.getElementById("encodingtext").value = encoding; - - // get the meta tags - var metaNodes = gDocument.getElementsByTagName("meta"); - var length = metaNodes.length; - - var metaGroup = document.getElementById("metaTags"); - if (!length) - metaGroup.collapsed = true; - else { - var metaTagsCaption = document.getElementById("metaTagsCaption"); - if (length == 1) - metaTagsCaption.label = gBundle.getString("generalMetaTag"); - else - metaTagsCaption.label = gBundle.getFormattedString("generalMetaTags", [length]); - var metaTree = document.getElementById("metatree"); - metaTree.view = gMetaView; - - for (var i = 0; i < length; i++) - gMetaView.addRow([metaNodes[i].name || metaNodes[i].httpEquiv, metaNodes[i].content]); - - metaGroup.collapsed = false; - } - - // get the date of last modification - var modifiedText = formatDate(gDocument.lastModified, gStrings.notSet); - document.getElementById("modifiedtext").value = modifiedText; - - // get cache info - var cacheKey = url.replace(/#.*$/, ""); - openCacheEntry(cacheKey, function(cacheEntry) { - var sizeText; - if (cacheEntry) { - var pageSize = cacheEntry.dataSize; - var kbSize = formatNumber(Math.round(pageSize / 1024 * 100) / 100); - sizeText = gBundle.getFormattedString("generalSize", [kbSize, formatNumber(pageSize)]); - } - setItemValue("sizetext", sizeText); - }); - - securityOnLoad(); -} - -//******** Generic Build-a-tab -// Assumes the views are empty. Only called once to build the tabs, and -// does so by farming the task off to another thread via setTimeout(). -// The actual work is done with a TreeWalker that calls doGrab() once for -// each element node in the document. - -var gFrameList = [ ]; - -function makeTabs(aDocument, aWindow) -{ - goThroughFrames(aDocument, aWindow); - processFrames(); -} - -function goThroughFrames(aDocument, aWindow) -{ - gFrameList.push(aDocument); - if (aWindow && aWindow.frames.length > 0) { - var num = aWindow.frames.length; - for (var i = 0; i < num; i++) - goThroughFrames(aWindow.frames[i].document, aWindow.frames[i]); // recurse through the frames - } -} - -function processFrames() -{ - if (gFrameList.length) { - var doc = gFrameList[0]; - onProcessFrame.forEach(function(func) { func(doc); }); - var iterator = doc.createTreeWalker(doc, NodeFilter.SHOW_ELEMENT, grabAll); - gFrameList.shift(); - setTimeout(doGrab, 10, iterator); - onFinished.push(selectImage); - } - else - onFinished.forEach(function(func) { func(); }); -} - -function doGrab(iterator) -{ - for (var i = 0; i < 500; ++i) - if (!iterator.nextNode()) { - processFrames(); - return; - } - - setTimeout(doGrab, 10, iterator); -} - -function addImage(url, type, alt, elem, isBg) -{ - if (!url) - return; - - if (!gImageHash.hasOwnProperty(url)) - gImageHash[url] = { }; - if (!gImageHash[url].hasOwnProperty(type)) - gImageHash[url][type] = { }; - if (!gImageHash[url][type].hasOwnProperty(alt)) { - gImageHash[url][type][alt] = gImageView.data.length; - var row = [url, type, -1, alt, 1, elem, isBg]; - gImageView.addRow(row); - - // Fill in cache data asynchronously - openCacheEntry(url, function(cacheEntry) { - // The data at row[2] corresponds to the data size. - if (cacheEntry) { - row[2] = cacheEntry.dataSize; - // Invalidate the row to trigger a repaint. - gImageView.tree.invalidateRow(gImageView.data.indexOf(row)); - } - }); - - // Add the observer, only once. - if (gImageView.data.length == 1) { - document.getElementById("mediaTab").hidden = false; - Components.classes["@mozilla.org/observer-service;1"] - .getService(Components.interfaces.nsIObserverService) - .addObserver(imagePermissionObserver, "perm-changed", false); - } - } - else { - var i = gImageHash[url][type][alt]; - gImageView.data[i][COL_IMAGE_COUNT]++; - if (elem == gImageElement) - gImageView.data[i][COL_IMAGE_NODE] = elem; - } -} - -function grabAll(elem) -{ - // check for images defined in CSS (e.g. background, borders), any node may have multiple - var computedStyle = elem.ownerDocument.defaultView.getComputedStyle(elem, ""); - - if (computedStyle) { - var addImgFunc = function (label, val) { - if (val.primitiveType == CSSPrimitiveValue.CSS_URI) { - addImage(val.getStringValue(), label, gStrings.notSet, elem, true); - } - else if (val.primitiveType == CSSPrimitiveValue.CSS_STRING) { - // This is for -moz-image-rect. - // TODO: Reimplement once bug 714757 is fixed - var strVal = val.getStringValue(); - if (strVal.search(/^.*url\(\"?/) > -1) { - url = strVal.replace(/^.*url\(\"?/,"").replace(/\"?\).*$/,""); - addImage(url, label, gStrings.notSet, elem, true); - } - } - else if (val.cssValueType == CSSValue.CSS_VALUE_LIST) { - // recursively resolve multiple nested CSS value lists - for (var i = 0; i < val.length; i++) - addImgFunc(label, val.item(i)); - } - }; - - addImgFunc(gStrings.mediaBGImg, computedStyle.getPropertyCSSValue("background-image")); - addImgFunc(gStrings.mediaBorderImg, computedStyle.getPropertyCSSValue("border-image-source")); - addImgFunc(gStrings.mediaListImg, computedStyle.getPropertyCSSValue("list-style-image")); - addImgFunc(gStrings.mediaCursor, computedStyle.getPropertyCSSValue("cursor")); - } - - // one swi^H^H^Hif-else to rule them all - if (elem instanceof HTMLImageElement) - addImage(elem.src, gStrings.mediaImg, - (elem.hasAttribute("alt")) ? elem.alt : gStrings.notSet, elem, false); - else if (elem instanceof SVGImageElement) { - try { - // Note: makeURLAbsolute will throw if either the baseURI is not a valid URI - // or the URI formed from the baseURI and the URL is not a valid URI - var href = makeURLAbsolute(elem.baseURI, elem.href.baseVal); - addImage(href, gStrings.mediaImg, "", elem, false); - } catch (e) { } - } - else if (elem instanceof HTMLVideoElement) { - addImage(elem.currentSrc, gStrings.mediaVideo, "", elem, false); - } - else if (elem instanceof HTMLAudioElement) { - addImage(elem.currentSrc, gStrings.mediaAudio, "", elem, false); - } - else if (elem instanceof HTMLLinkElement) { - if (elem.rel && /\bicon\b/i.test(elem.rel)) - addImage(elem.href, gStrings.mediaLink, "", elem, false); - } - else if (elem instanceof HTMLInputElement || elem instanceof HTMLButtonElement) { - if (elem.type.toLowerCase() == "image") - addImage(elem.src, gStrings.mediaInput, - (elem.hasAttribute("alt")) ? elem.alt : gStrings.notSet, elem, false); - } - else if (elem instanceof HTMLObjectElement) - addImage(elem.data, gStrings.mediaObject, getValueText(elem), elem, false); - else if (elem instanceof HTMLEmbedElement) - addImage(elem.src, gStrings.mediaEmbed, "", elem, false); - - onProcessElement.forEach(function(func) { func(elem); }); - - return NodeFilter.FILTER_ACCEPT; -} - -//******** Link Stuff -function openURL(target) -{ - var url = target.parentNode.childNodes[2].value; - window.open(url, "_blank", "chrome"); -} - -function onBeginLinkDrag(event,urlField,descField) -{ - if (event.originalTarget.localName != "treechildren") - return; - - var tree = event.target; - if (!("treeBoxObject" in tree)) - tree = tree.parentNode; - - var row = tree.treeBoxObject.getRowAt(event.clientX, event.clientY); - if (row == -1) - return; - - // Adding URL flavor - var col = tree.columns[urlField]; - var url = tree.view.getCellText(row, col); - col = tree.columns[descField]; - var desc = tree.view.getCellText(row, col); - - var dt = event.dataTransfer; - dt.setData("text/x-moz-url", url + "\n" + desc); - dt.setData("text/url-list", url); - dt.setData("text/plain", url); -} - -//******** Image Stuff -function getSelectedRows(tree) -{ - var start = { }; - var end = { }; - var numRanges = tree.view.selection.getRangeCount(); - - var rowArray = [ ]; - for (var t = 0; t < numRanges; t++) { - tree.view.selection.getRangeAt(t, start, end); - for (var v = start.value; v <= end.value; v++) - rowArray.push(v); - } - - return rowArray; -} - -function getSelectedRow(tree) -{ - var rows = getSelectedRows(tree); - return (rows.length == 1) ? rows[0] : -1; -} - -function selectSaveFolder(aCallback) -{ - const nsILocalFile = Components.interfaces.nsILocalFile; - const nsIFilePicker = Components.interfaces.nsIFilePicker; - let titleText = gBundle.getString("mediaSelectFolder"); - let fp = Components.classes["@mozilla.org/filepicker;1"]. - createInstance(nsIFilePicker); - let fpCallback = function fpCallback_done(aResult) { - if (aResult == nsIFilePicker.returnOK) { - aCallback(fp.file.QueryInterface(nsILocalFile)); - } else { - aCallback(null); - } - }; - - fp.init(window, titleText, nsIFilePicker.modeGetFolder); - fp.appendFilters(nsIFilePicker.filterAll); - try { - let prefs = Components.classes[PREFERENCES_CONTRACTID]. - getService(Components.interfaces.nsIPrefBranch); - let initialDir = prefs.getComplexValue("browser.download.dir", nsILocalFile); - if (initialDir) { - fp.displayDirectory = initialDir; - } - } catch (ex) { - } - fp.open(fpCallback); -} - -function saveMedia() -{ - var tree = document.getElementById("imagetree"); - var rowArray = getSelectedRows(tree); - if (rowArray.length == 1) { - var row = rowArray[0]; - var item = gImageView.data[row][COL_IMAGE_NODE]; - var url = gImageView.data[row][COL_IMAGE_ADDRESS]; - - if (url) { - var titleKey = "SaveImageTitle"; - - if (item instanceof HTMLVideoElement) - titleKey = "SaveVideoTitle"; - else if (item instanceof HTMLAudioElement) - titleKey = "SaveAudioTitle"; - - saveURL(url, null, titleKey, false, false, makeURI(item.baseURI), gDocument); - } - } else { - selectSaveFolder(function(aDirectory) { - if (aDirectory) { - var saveAnImage = function(aURIString, aChosenData, aBaseURI) { - internalSave(aURIString, null, null, null, null, false, "SaveImageTitle", - aChosenData, aBaseURI, gDocument); - }; - - for (var i = 0; i < rowArray.length; i++) { - var v = rowArray[i]; - var dir = aDirectory.clone(); - var item = gImageView.data[v][COL_IMAGE_NODE]; - var uriString = gImageView.data[v][COL_IMAGE_ADDRESS]; - var uri = makeURI(uriString); - - try { - uri.QueryInterface(Components.interfaces.nsIURL); - dir.append(decodeURIComponent(uri.fileName)); - } catch(ex) { - /* data: uris */ - } - - if (i == 0) { - saveAnImage(uriString, new AutoChosen(dir, uri), makeURI(item.baseURI)); - } else { - // This delay is a hack which prevents the download manager - // from opening many times. See bug 377339. - setTimeout(saveAnImage, 200, uriString, new AutoChosen(dir, uri), - makeURI(item.baseURI)); - } - } - } - }); - } -} - -function onBlockImage() -{ - var permissionManager = Components.classes[PERMISSION_CONTRACTID] - .getService(nsIPermissionManager); - - var checkbox = document.getElementById("blockImage"); - var uri = makeURI(document.getElementById("imageurltext").value); - if (checkbox.checked) - permissionManager.add(uri, "image", nsIPermissionManager.DENY_ACTION); - else - permissionManager.remove(uri, "image"); -} - -function onImageSelect() -{ - var previewBox = document.getElementById("mediaPreviewBox"); - var mediaSaveBox = document.getElementById("mediaSaveBox"); - var splitter = document.getElementById("mediaSplitter"); - var tree = document.getElementById("imagetree"); - var count = tree.view.selection.count; - if (count == 0) { - previewBox.collapsed = true; - mediaSaveBox.collapsed = true; - splitter.collapsed = true; - tree.flex = 1; - } - else if (count > 1) { - splitter.collapsed = true; - previewBox.collapsed = true; - mediaSaveBox.collapsed = false; - tree.flex = 1; - } - else { - mediaSaveBox.collapsed = true; - splitter.collapsed = false; - previewBox.collapsed = false; - tree.flex = 0; - makePreview(getSelectedRows(tree)[0]); - } -} - -function makePreview(row) -{ - var imageTree = document.getElementById("imagetree"); - var item = gImageView.data[row][COL_IMAGE_NODE]; - var url = gImageView.data[row][COL_IMAGE_ADDRESS]; - var isBG = gImageView.data[row][COL_IMAGE_BG]; - var isAudio = false; - - setItemValue("imageurltext", url); - - var imageText; - if (!isBG && - !(item instanceof SVGImageElement) && - !(gDocument instanceof ImageDocument)) { - imageText = item.title || item.alt; - - if (!imageText && !(item instanceof HTMLImageElement)) - imageText = getValueText(item); - } - setItemValue("imagetext", imageText); - - setItemValue("imagelongdesctext", item.longDesc); - - // get cache info - var cacheKey = url.replace(/#.*$/, ""); - openCacheEntry(cacheKey, function(cacheEntry) { - // find out the file size - var sizeText; - if (cacheEntry) { - var imageSize = cacheEntry.dataSize; - var kbSize = Math.round(imageSize / 1024 * 100) / 100; - sizeText = gBundle.getFormattedString("generalSize", - [formatNumber(kbSize), formatNumber(imageSize)]); - } - else - sizeText = gBundle.getString("mediaUnknownNotCached"); - setItemValue("imagesizetext", sizeText); - - var mimeType; - var numFrames = 1; - if (item instanceof HTMLObjectElement || - item instanceof HTMLEmbedElement || - item instanceof HTMLLinkElement) - mimeType = item.type; - - if (!mimeType && !isBG && item instanceof nsIImageLoadingContent) { - var imageRequest = item.getRequest(nsIImageLoadingContent.CURRENT_REQUEST); - if (imageRequest) { - mimeType = imageRequest.mimeType; - var image = imageRequest.image; - if (image) - numFrames = image.numFrames; - } - } - - if (!mimeType) - mimeType = getContentTypeFromHeaders(cacheEntry); - - // if we have a data url, get the MIME type from the url - if (!mimeType && url.startsWith("data:")) { - let dataMimeType = /^data:(image\/[^;,]+)/i.exec(url); - if (dataMimeType) - mimeType = dataMimeType[1].toLowerCase(); - } - - var imageType; - if (mimeType) { - // We found the type, try to display it nicely - let imageMimeType = /^image\/(.*)/i.exec(mimeType); - if (imageMimeType) { - imageType = imageMimeType[1].toUpperCase(); - if (numFrames > 1) - imageType = gBundle.getFormattedString("mediaAnimatedImageType", - [imageType, numFrames]); - else - imageType = gBundle.getFormattedString("mediaImageType", [imageType]); - } - else { - // the MIME type doesn't begin with image/, display the raw type - imageType = mimeType; - } - } - else { - // We couldn't find the type, fall back to the value in the treeview - imageType = gImageView.data[row][COL_IMAGE_TYPE]; - } - setItemValue("imagetypetext", imageType); - - var imageContainer = document.getElementById("theimagecontainer"); - var oldImage = document.getElementById("thepreviewimage"); - - var isProtocolAllowed = checkProtocol(gImageView.data[row]); - - var newImage = new Image; - newImage.id = "thepreviewimage"; - var physWidth = 0, physHeight = 0; - var width = 0, height = 0; - - if ((item instanceof HTMLLinkElement || item instanceof HTMLInputElement || - item instanceof HTMLImageElement || - item instanceof SVGImageElement || - (item instanceof HTMLObjectElement && mimeType && mimeType.startsWith("image/")) || isBG) && isProtocolAllowed) { - newImage.setAttribute("src", url); - physWidth = newImage.width || 0; - physHeight = newImage.height || 0; - - // "width" and "height" attributes must be set to newImage, - // even if there is no "width" or "height attribute in item; - // otherwise, the preview image cannot be displayed correctly. - if (!isBG) { - newImage.width = ("width" in item && item.width) || newImage.naturalWidth; - newImage.height = ("height" in item && item.height) || newImage.naturalHeight; - } - else { - // the Width and Height of an HTML tag should not be used for its background image - // (for example, "table" can have "width" or "height" attributes) - newImage.width = newImage.naturalWidth; - newImage.height = newImage.naturalHeight; - } - - if (item instanceof SVGImageElement) { - newImage.width = item.width.baseVal.value; - newImage.height = item.height.baseVal.value; - } - - width = newImage.width; - height = newImage.height; - - document.getElementById("theimagecontainer").collapsed = false - document.getElementById("brokenimagecontainer").collapsed = true; - } - else if (item instanceof HTMLVideoElement && isProtocolAllowed) { - newImage = document.createElementNS("http://www.w3.org/1999/xhtml", "video"); - newImage.id = "thepreviewimage"; - newImage.src = url; - newImage.controls = true; - width = physWidth = item.videoWidth; - height = physHeight = item.videoHeight; - - document.getElementById("theimagecontainer").collapsed = false; - document.getElementById("brokenimagecontainer").collapsed = true; - } - else if (item instanceof HTMLAudioElement && isProtocolAllowed) { - newImage = new Audio; - newImage.id = "thepreviewimage"; - newImage.src = url; - newImage.controls = true; - isAudio = true; - - document.getElementById("theimagecontainer").collapsed = false; - document.getElementById("brokenimagecontainer").collapsed = true; - } - else { - // fallback image for protocols not allowed (e.g., javascript:) - // or elements not [yet] handled (e.g., object, embed). - document.getElementById("brokenimagecontainer").collapsed = false; - document.getElementById("theimagecontainer").collapsed = true; - } - - var imageSize = ""; - if (url && !isAudio) { - if (width != physWidth || height != physHeight) { - imageSize = gBundle.getFormattedString("mediaDimensionsScaled", - [formatNumber(physWidth), - formatNumber(physHeight), - formatNumber(width), - formatNumber(height)]); - } - else { - imageSize = gBundle.getFormattedString("mediaDimensions", - [formatNumber(width), - formatNumber(height)]); - } - } - setItemValue("imagedimensiontext", imageSize); - - makeBlockImage(url); - - imageContainer.removeChild(oldImage); - imageContainer.appendChild(newImage); - - onImagePreviewShown.forEach(function(func) { func(); }); - }); -} - -function makeBlockImage(url) -{ - var permissionManager = Components.classes[PERMISSION_CONTRACTID] - .getService(nsIPermissionManager); - var prefs = Components.classes[PREFERENCES_CONTRACTID] - .getService(Components.interfaces.nsIPrefBranch); - - var checkbox = document.getElementById("blockImage"); - var imagePref = prefs.getIntPref("permissions.default.image"); - if (!(/^https?:/.test(url)) || imagePref == 2) - // We can't block the images from this host because either is is not - // for http(s) or we don't load images at all - checkbox.hidden = true; - else { - var uri = makeURI(url); - if (uri.host) { - checkbox.hidden = false; - checkbox.label = gBundle.getFormattedString("mediaBlockImage", [uri.host]); - var perm = permissionManager.testPermission(uri, "image"); - checkbox.checked = perm == nsIPermissionManager.DENY_ACTION; - } - else - checkbox.hidden = true; - } -} - -var imagePermissionObserver = { - observe: function (aSubject, aTopic, aData) - { - if (document.getElementById("mediaPreviewBox").collapsed) - return; - - if (aTopic == "perm-changed") { - var permission = aSubject.QueryInterface(Components.interfaces.nsIPermission); - if (permission.type == "image") { - var imageTree = document.getElementById("imagetree"); - var row = getSelectedRow(imageTree); - var item = gImageView.data[row][COL_IMAGE_NODE]; - var url = gImageView.data[row][COL_IMAGE_ADDRESS]; - if (permission.matchesURI(makeURI(url), true)) { - makeBlockImage(url); - } - } - } - } -} - -function getContentTypeFromHeaders(cacheEntryDescriptor) -{ - if (!cacheEntryDescriptor) - return null; - - return (/^Content-Type:\s*(.*?)\s*(?:\;|$)/mi - .exec(cacheEntryDescriptor.getMetaDataElement("response-head")))[1]; -} - -//******** Other Misc Stuff -// Modified from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html -// parse a node to extract the contents of the node -function getValueText(node) -{ - var valueText = ""; - - // form input elements don't generally contain information that is useful to our callers, so return nothing - if (node instanceof HTMLInputElement || - node instanceof HTMLSelectElement || - node instanceof HTMLTextAreaElement) - return valueText; - - // otherwise recurse for each child - var length = node.childNodes.length; - for (var i = 0; i < length; i++) { - var childNode = node.childNodes[i]; - var nodeType = childNode.nodeType; - - // text nodes are where the goods are - if (nodeType == Node.TEXT_NODE) - valueText += " " + childNode.nodeValue; - // and elements can have more text inside them - else if (nodeType == Node.ELEMENT_NODE) { - // images are special, we want to capture the alt text as if the image weren't there - if (childNode instanceof HTMLImageElement) - valueText += " " + getAltText(childNode); - else - valueText += " " + getValueText(childNode); - } - } - - return stripWS(valueText); -} - -// Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html -// traverse the tree in search of an img or area element and grab its alt tag -function getAltText(node) -{ - var altText = ""; - - if (node.alt) - return node.alt; - var length = node.childNodes.length; - for (var i = 0; i < length; i++) - if ((altText = getAltText(node.childNodes[i]) != undefined)) // stupid js warning... - return altText; - return ""; -} - -// Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html -// strip leading and trailing whitespace, and replace multiple consecutive whitespace characters with a single space -function stripWS(text) -{ - var middleRE = /\s+/g; - var endRE = /(^\s+)|(\s+$)/g; - - text = text.replace(middleRE, " "); - return text.replace(endRE, ""); -} - -function setItemValue(id, value) -{ - var item = document.getElementById(id); - if (value) { - item.parentNode.collapsed = false; - item.value = value; - } - else - item.parentNode.collapsed = true; -} - -function formatNumber(number) -{ - return (+number).toLocaleString(); // coerce number to a numeric value before calling toLocaleString() -} - -function formatDate(datestr, unknown) -{ - // scriptable date formatter, for pretty printing dates - var dateService = Components.classes["@mozilla.org/intl/scriptabledateformat;1"] - .getService(Components.interfaces.nsIScriptableDateFormat); - - var date = new Date(datestr); - if (!date.valueOf()) - return unknown; - - return dateService.FormatDateTime("", dateService.dateFormatLong, - dateService.timeFormatSeconds, - date.getFullYear(), date.getMonth()+1, date.getDate(), - date.getHours(), date.getMinutes(), date.getSeconds()); -} - -function doCopy() -{ - if (!gClipboardHelper) - return; - - var elem = document.commandDispatcher.focusedElement; - - if (elem && "treeBoxObject" in elem) { - var view = elem.view; - var selection = view.selection; - var text = [], tmp = ''; - var min = {}, max = {}; - - var count = selection.getRangeCount(); - - for (var i = 0; i < count; i++) { - selection.getRangeAt(i, min, max); - - for (var row = min.value; row <= max.value; row++) { - view.performActionOnRow("copy", row); - - tmp = elem.getAttribute("copybuffer"); - if (tmp) - text.push(tmp); - elem.removeAttribute("copybuffer"); - } - } - gClipboardHelper.copyString(text.join("\n"), document); - } -} - -function doSelectAll() -{ - var elem = document.commandDispatcher.focusedElement; - - if (elem && "treeBoxObject" in elem) - elem.view.selection.selectAll(); -} - -function selectImage() -{ - if (!gImageElement) - return; - - var tree = document.getElementById("imagetree"); - for (var i = 0; i < tree.view.rowCount; i++) { - if (gImageElement == gImageView.data[i][COL_IMAGE_NODE] && - !gImageView.data[i][COL_IMAGE_BG]) { - tree.view.selection.select(i); - tree.treeBoxObject.ensureRowIsVisible(i); - tree.focus(); - return; - } - } -} - -function checkProtocol(img) -{ - var url = img[COL_IMAGE_ADDRESS]; - return /^data:image\//i.test(url) || - /^(https?|ftp|file|about|chrome|resource):/.test(url); -} diff --git a/components/pageinfo/pageInfo.xml b/components/pageinfo/pageInfo.xml deleted file mode 100644 index 20d3300..0000000 --- a/components/pageinfo/pageInfo.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0"?> -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - - -<bindings id="pageInfoBindings" - xmlns="http://www.mozilla.org/xbl" - xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:xbl="http://www.mozilla.org/xbl"> - - <!-- based on preferences.xml paneButton --> - <binding id="viewbutton" extends="chrome://global/content/bindings/radio.xml#radio"> - <content> - <xul:image class="viewButtonIcon" xbl:inherits="src"/> - <xul:label class="viewButtonLabel" xbl:inherits="value=label"/> - </content> - <implementation implements="nsIAccessibleProvider"> - <property name="accessibleType" readonly="true"> - <getter> - <![CDATA[ - return Components.interfaces.nsIAccessibleProvider.XULListitem; - ]]> - </getter> - </property> - </implementation> - </binding> - -</bindings> diff --git a/components/pageinfo/pageInfo.xul b/components/pageinfo/pageInfo.xul deleted file mode 100644 index c7c486a..0000000 --- a/components/pageinfo/pageInfo.xul +++ /dev/null @@ -1,507 +0,0 @@ -<?xml version="1.0"?> -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -<?xml-stylesheet href="chrome://browser/content/pageinfo/pageInfo.css" type="text/css"?> -<?xml-stylesheet href="chrome://browser/skin/pageInfo.css" type="text/css"?> - -<!DOCTYPE window [ - <!ENTITY % pageInfoDTD SYSTEM "chrome://browser/locale/pageInfo.dtd"> - %pageInfoDTD; -]> - -#ifdef XP_MACOSX -<?xul-overlay href="chrome://browser/content/macBrowserOverlay.xul"?> -#endif - -<window id="main-window" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - windowtype="Browser:page-info" - onload="onLoadPageInfo()" - onunload="onUnloadPageInfo()" - align="stretch" - screenX="10" screenY="10" - width="&pageInfoWindow.width;" height="&pageInfoWindow.height;" - persist="screenX screenY width height sizemode"> - - <script type="application/javascript" src="chrome://global/content/globalOverlay.js"/> - <script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/> - <script type="application/javascript" src="chrome://global/content/treeUtils.js"/> - <script type="application/javascript" src="chrome://browser/content/pageinfo/pageInfo.js"/> - <script type="application/javascript" src="chrome://browser/content/pageinfo/feeds.js"/> - <script type="application/javascript" src="chrome://browser/content/pageinfo/permissions.js"/> - <script type="application/javascript" src="chrome://browser/content/pageinfo/security.js"/> - <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/> - - <stringbundleset id="pageinfobundleset"> - <stringbundle id="pageinfobundle" src="chrome://browser/locale/pageInfo.properties"/> - <stringbundle id="pkiBundle" src="chrome://pippki/locale/pippki.properties"/> - <stringbundle id="browserBundle" src="chrome://browser/locale/browser.properties"/> - </stringbundleset> - - <commandset id="pageInfoCommandSet"> - <command id="cmd_close" oncommand="window.close();"/> - <command id="cmd_help" oncommand="doHelpButton();"/> - <command id="cmd_copy" oncommand="doCopy();"/> - <command id="cmd_selectall" oncommand="doSelectAll();"/> - - <!-- permissions tab --> - <command id="cmd_imageDef" oncommand="onCheckboxClick('image');"/> - <command id="cmd_popupDef" oncommand="onCheckboxClick('popup');"/> - <command id="cmd_cookieDef" oncommand="onCheckboxClick('cookie');"/> - <command id="cmd_desktop-notificationDef" oncommand="onCheckboxClick('desktop-notification');"/> - <command id="cmd_installDef" oncommand="onCheckboxClick('install');"/> - <command id="cmd_geoDef" oncommand="onCheckboxClick('geo');"/> - <command id="cmd_pluginsDef" oncommand="onCheckboxClick('plugins');"/> - <command id="cmd_imageToggle" oncommand="onRadioClick('image');"/> - <command id="cmd_popupToggle" oncommand="onRadioClick('popup');"/> - <command id="cmd_cookieToggle" oncommand="onRadioClick('cookie');"/> - <command id="cmd_desktop-notificationToggle" oncommand="onRadioClick('desktop-notification');"/> - <command id="cmd_installToggle" oncommand="onRadioClick('install');"/> - <command id="cmd_geoToggle" oncommand="onRadioClick('geo');"/> - <command id="cmd_pluginsToggle" oncommand="onPluginRadioClick(event);"/> - </commandset> - - <keyset id="pageInfoKeySet"> - <key key="&closeWindow.key;" modifiers="accel" command="cmd_close"/> - <key keycode="VK_ESCAPE" command="cmd_close"/> -#ifdef XP_MACOSX - <key key="." modifiers="meta" command="cmd_close"/> -#else - <key keycode="VK_F1" command="cmd_help"/> -#endif - <key key="©.key;" modifiers="accel" command="cmd_copy"/> - <key key="&selectall.key;" modifiers="accel" command="cmd_selectall"/> - <key key="&selectall.key;" modifiers="alt" command="cmd_selectall"/> - </keyset> - - <menupopup id="picontext"> - <menuitem id="menu_selectall" label="&selectall.label;" command="cmd_selectall" accesskey="&selectall.accesskey;"/> - <menuitem id="menu_copy" label="©.label;" command="cmd_copy" accesskey="©.accesskey;"/> - </menupopup> - - <windowdragbox id="topBar" class="viewGroupWrapper"> - <radiogroup id="viewGroup" class="chromeclass-toolbar" orient="horizontal"> - <radio id="generalTab" label="&generalTab;" accesskey="&generalTab.accesskey;" - oncommand="showTab('general');"/> - <radio id="mediaTab" label="&mediaTab;" accesskey="&mediaTab.accesskey;" - oncommand="showTab('media');" hidden="true"/> - <radio id="feedTab" label="&feedTab;" accesskey="&feedTab.accesskey;" - oncommand="showTab('feed');" hidden="true"/> - <radio id="permTab" label="&permTab;" accesskey="&permTab.accesskey;" - oncommand="showTab('perm');"/> - <radio id="securityTab" label="&securityTab;" accesskey="&securityTab.accesskey;" - oncommand="showTab('security');"/> - <!-- Others added by overlay --> - </radiogroup> - </windowdragbox> - - <deck id="mainDeck" flex="1"> - <!-- General page information --> - <vbox id="generalPanel"> - <textbox class="header" readonly="true" id="titletext"/> - <grid id="generalGrid"> - <columns> - <column/> - <column class="gridSeparator"/> - <column flex="1"/> - </columns> - <rows id="generalRows"> - <row id="generalURLRow"> - <label control="urltext" value="&generalURL;"/> - <separator/> - <textbox readonly="true" id="urltext"/> - </row> - <row id="generalSeparatorRow1"> - <separator class="thin"/> - </row> - <row id="generalTypeRow"> - <label control="typetext" value="&generalType;"/> - <separator/> - <textbox readonly="true" id="typetext"/> - </row> - <row id="generalModeRow"> - <label control="modetext" value="&generalMode;"/> - <separator/> - <textbox readonly="true" crop="end" id="modetext"/> - </row> - <row id="generalEncodingRow"> - <label control="encodingtext" value="&generalEncoding;"/> - <separator/> - <textbox readonly="true" id="encodingtext"/> - </row> - <row id="generalSizeRow"> - <label control="sizetext" value="&generalSize;"/> - <separator/> - <textbox readonly="true" id="sizetext"/> - </row> - <row id="generalReferrerRow"> - <label control="refertext" value="&generalReferrer;"/> - <separator/> - <textbox readonly="true" id="refertext"/> - </row> - <row id="generalSeparatorRow2"> - <separator class="thin"/> - </row> - <row id="generalModifiedRow"> - <label control="modifiedtext" value="&generalModified;"/> - <separator/> - <textbox readonly="true" id="modifiedtext"/> - </row> - </rows> - </grid> - <separator class="thin"/> - <groupbox id="metaTags" flex="1" class="collapsable treebox"> - <caption id="metaTagsCaption" onclick="toggleGroupbox('metaTags');"/> - <tree id="metatree" flex="1" hidecolumnpicker="true" contextmenu="picontext"> - <treecols> - <treecol id="meta-name" label="&generalMetaName;" - persist="width" flex="1" - onclick="gMetaView.onPageMediaSort('meta-name');"/> - <splitter class="tree-splitter"/> - <treecol id="meta-content" label="&generalMetaContent;" - persist="width" flex="4" - onclick="gMetaView.onPageMediaSort('meta-content');"/> - </treecols> - <treechildren id="metatreechildren" flex="1"/> - </tree> - </groupbox> - <groupbox id="securityBox"> - <caption id="securityBoxCaption" label="&securityHeader;"/> - <description id="general-security-identity" class="header"/> - <description id="general-security-privacy" class="header"/> - <hbox id="securityDetailsButtonBox" align="right"> - <button id="security-view-details" label="&generalSecurityDetails;" - accesskey="&generalSecurityDetails.accesskey;" - oncommand="onClickMore();"/> - </hbox> - </groupbox> - </vbox> - - <!-- Media information --> - <vbox id="mediaPanel"> - <tree id="imagetree" onselect="onImageSelect();" contextmenu="picontext" - ondragstart="onBeginLinkDrag(event,'image-address','image-alt')"> - <treecols> - <treecol sortSeparators="true" primary="true" persist="width" flex="10" - width="10" id="image-address" label="&mediaAddress;" - onclick="gImageView.onPageMediaSort('image-address');"/> - <splitter class="tree-splitter"/> - <treecol sortSeparators="true" persist="hidden width" flex="2" - width="2" id="image-type" label="&mediaType;" - onclick="gImageView.onPageMediaSort('image-type');"/> - <splitter class="tree-splitter"/> - <treecol sortSeparators="true" hidden="true" persist="hidden width" flex="2" - width="2" id="image-size" label="&mediaSize;" value="size" - onclick="gImageView.onPageMediaSort('image-size');"/> - <splitter class="tree-splitter"/> - <treecol sortSeparators="true" hidden="true" persist="hidden width" flex="4" - width="4" id="image-alt" label="&mediaAltHeader;" - onclick="gImageView.onPageMediaSort('image-alt');"/> - <splitter class="tree-splitter"/> - <treecol sortSeparators="true" hidden="true" persist="hidden width" flex="1" - width="1" id="image-count" label="&mediaCount;" - onclick="gImageView.onPageMediaSort('image-count');"/> - </treecols> - <treechildren id="imagetreechildren" flex="1"/> - </tree> - <splitter orient="vertical" id="mediaSplitter"/> - <vbox flex="1" id="mediaPreviewBox" collapsed="true"> - <grid id="mediaGrid"> - <columns> - <column id="mediaLabelColumn"/> - <column class="gridSeparator"/> - <column flex="1"/> - </columns> - <rows id="mediaRows"> - <row id="mediaLocationRow"> - <label control="imageurltext" value="&mediaLocation;"/> - <separator/> - <textbox readonly="true" id="imageurltext"/> - </row> - <row id="mediaTypeRow"> - <label control="imagetypetext" value="&generalType;"/> - <separator/> - <textbox readonly="true" id="imagetypetext"/> - </row> - <row id="mediaSizeRow"> - <label control="imagesizetext" value="&generalSize;"/> - <separator/> - <textbox readonly="true" id="imagesizetext"/> - </row> - <row id="mediaDimensionRow"> - <label control="imagedimensiontext" value="&mediaDimension;"/> - <separator/> - <textbox readonly="true" id="imagedimensiontext"/> - </row> - <row id="mediaTextRow"> - <label control="imagetext" value="&mediaText;"/> - <separator/> - <textbox readonly="true" id="imagetext"/> - </row> - <row id="mediaLongdescRow"> - <label control="imagelongdesctext" value="&mediaLongdesc;"/> - <separator/> - <textbox readonly="true" id="imagelongdesctext"/> - </row> - </rows> - </grid> - <hbox id="imageSaveBox" align="end"> - <vbox id="blockImageBox"> - <checkbox id="blockImage" hidden="true" oncommand="onBlockImage()" - accesskey="&mediaBlockImage.accesskey;"/> - <label control="thepreviewimage" value="&mediaPreview;" class="header"/> - </vbox> - <spacer id="imageSaveBoxSpacer" flex="1"/> - <button label="&mediaSaveAs;" accesskey="&mediaSaveAs.accesskey;" - icon="save" id="imagesaveasbutton" - oncommand="saveMedia();"/> - </hbox> - <vbox id="imagecontainerbox" class="inset iframe" flex="1" pack="center"> - <hbox id="theimagecontainer" pack="center"> - <image id="thepreviewimage"/> - </hbox> - <hbox id="brokenimagecontainer" pack="center" collapsed="true"> - <image id="brokenimage" src="resource://gre-resources/broken-image.png"/> - </hbox> - </vbox> - </vbox> - <hbox id="mediaSaveBox" collapsed="true"> - <spacer id="mediaSaveBoxSpacer" flex="1"/> - <button label="&mediaSaveAs;" accesskey="&mediaSaveAs2.accesskey;" - icon="save" id="mediasaveasbutton" - oncommand="saveMedia();"/> - </hbox> - </vbox> - - <!-- Feeds --> - <vbox id="feedPanel"> - <richlistbox id="feedListbox" flex="1"/> - </vbox> - - <!-- Permissions --> - <vbox id="permPanel"> - <hbox id="permHostBox"> - <label value="&permissionsFor;" control="hostText" /> - <textbox id="hostText" class="header" readonly="true" - crop="end" flex="1"/> - </hbox> - - <vbox id="permList" flex="1"> - <vbox class="permission" id="permImageRow"> - <label class="permissionLabel" id="permImageLabel" - value="&permImage;" control="imageRadioGroup"/> - <hbox id="permImageBox" role="group" aria-labelledby="permImageLabel"> - <checkbox id="imageDef" command="cmd_imageDef" label="&permUseDefault;"/> - <spacer flex="1"/> - <radiogroup id="imageRadioGroup" orient="horizontal"> - <radio id="image#1" command="cmd_imageToggle" label="&permAllow;"/> - <radio id="image#2" command="cmd_imageToggle" label="&permBlock;"/> - </radiogroup> - </hbox> - </vbox> - <vbox class="permission" id="permPopupRow"> - <label class="permissionLabel" id="permPopupLabel" - value="&permPopup;" control="popupRadioGroup"/> - <hbox id="permPopupBox" role="group" aria-labelledby="permPopupLabel"> - <checkbox id="popupDef" command="cmd_popupDef" label="&permUseDefault;"/> - <spacer flex="1"/> - <radiogroup id="popupRadioGroup" orient="horizontal"> - <radio id="popup#1" command="cmd_popupToggle" label="&permAllow;"/> - <radio id="popup#2" command="cmd_popupToggle" label="&permBlock;"/> - </radiogroup> - </hbox> - </vbox> - <vbox class="permission" id="permCookieRow"> - <label class="permissionLabel" id="permCookieLabel" - value="&permCookie;" control="cookieRadioGroup"/> - <hbox id="permCookieBox" role="group" aria-labelledby="permCookieLabel"> - <checkbox id="cookieDef" command="cmd_cookieDef" label="&permUseDefault;"/> - <spacer flex="1"/> - <radiogroup id="cookieRadioGroup" orient="horizontal"> - <radio id="cookie#1" command="cmd_cookieToggle" label="&permAllow;"/> - <radio id="cookie#8" command="cmd_cookieToggle" label="&permAllowSession;"/> - <radio id="cookie#9" command="cmd_cookieToggle" label="&permAllowFirstPartyOnly;"/> - <radio id="cookie#2" command="cmd_cookieToggle" label="&permBlock;"/> - </radiogroup> - </hbox> - </vbox> - <vbox class="permission" id="permNotificationRow"> - <label class="permissionLabel" id="permNotificationLabel" - value="&permNotifications;" control="desktop-notificationRadioGroup"/> - <hbox role="group" aria-labelledby="permNotificationLabel"> - <checkbox id="desktop-notificationDef" command="cmd_desktop-notificationDef" label="&permUseDefault;"/> - <spacer flex="1"/> - <radiogroup id="desktop-notificationRadioGroup" orient="horizontal"> - <radio id="desktop-notification#0" command="cmd_desktop-notificationToggle" label="&permAskAlways;"/> - <radio id="desktop-notification#1" command="cmd_desktop-notificationToggle" label="&permAllow;"/> - <radio id="desktop-notification#2" command="cmd_desktop-notificationToggle" label="&permBlock;"/> - </radiogroup> - </hbox> - </vbox> - <vbox class="permission" id="permInstallRow"> - <label class="permissionLabel" id="permInstallLabel" - value="&permInstall;" control="installRadioGroup"/> - <hbox id="permInstallBox" role="group" aria-labelledby="permInstallLabel"> - <checkbox id="installDef" command="cmd_installDef" label="&permUseDefault;"/> - <spacer flex="1"/> - <radiogroup id="installRadioGroup" orient="horizontal"> - <radio id="install#1" command="cmd_installToggle" label="&permAllow;"/> - <radio id="install#2" command="cmd_installToggle" label="&permBlock;"/> - </radiogroup> - </hbox> - </vbox> - <vbox class="permission" id="permGeoRow" > - <label class="permissionLabel" id="permGeoLabel" - value="&permGeo;" control="geoRadioGroup"/> - <hbox id="permGeoBox" role="group" aria-labelledby="permGeoLabel"> - <checkbox id="geoDef" command="cmd_geoDef" label="&permAskAlways;"/> - <spacer flex="1"/> - <radiogroup id="geoRadioGroup" orient="horizontal"> - <radio id="geo#1" command="cmd_geoToggle" label="&permAllow;"/> - <radio id="geo#2" command="cmd_geoToggle" label="&permBlock;"/> - </radiogroup> - </hbox> - </vbox> - <vbox class="permission" id="permPluginsRow"> - <label class="permissionLabel" id="permPluginsLabel" - value="&permPlugins;" control="pluginsRadioGroup"/> - <hbox id="permPluginTemplate" role="group" aria-labelledby="permPluginsLabel" align="baseline"> - <label class="permPluginTemplateLabel"/> - <spacer flex="1"/> - <radiogroup class="permPluginTemplateRadioGroup" orient="horizontal" command="cmd_pluginsToggle"> - <radio class="permPluginTemplateRadioDefault" label="&permUseDefault;"/> - <radio class="permPluginTemplateRadioAsk" label="&permAskAlways;"/> - <radio class="permPluginTemplateRadioAllow" label="&permAllow;"/> - <radio class="permPluginTemplateRadioBlock" label="&permBlock;"/> - </radiogroup> - </hbox> - </vbox> - </vbox> - </vbox> - - <!-- Security & Privacy --> - <vbox id="securityPanel"> - <!-- Identity Section --> - <groupbox id="security-identity-groupbox" flex="1"> - <caption id="security-identity" label="&securityView.identity.header;"/> - <grid id="security-identity-grid" flex="1"> - <columns> - <column/> - <column flex="1"/> - </columns> - <rows id="security-identity-rows"> - <!-- Domain --> - <row id="security-identity-domain-row"> - <label id="security-identity-domain-label" - class="fieldLabel" - value="&securityView.identity.domain;" - control="security-identity-domain-value"/> - <textbox id="security-identity-domain-value" - class="fieldValue" readonly="true"/> - </row> - <!-- Owner --> - <row id="security-identity-owner-row"> - <label id="security-identity-owner-label" - class="fieldLabel" - value="&securityView.identity.owner;" - control="security-identity-owner-value"/> - <textbox id="security-identity-owner-value" - class="fieldValue" readonly="true"/> - </row> - <!-- Verifier --> - <row id="security-identity-verifier-row"> - <label id="security-identity-verifier-label" - class="fieldLabel" - value="&securityView.identity.verifier;" - control="security-identity-verifier-value"/> - <textbox id="security-identity-verifier-value" - class="fieldValue" readonly="true" /> - </row> - </rows> - </grid> - <spacer flex="1"/> - <!-- Cert button --> - <hbox id="security-view-cert-box" pack="end"> - <button id="security-view-cert" label="&securityView.certView;" - accesskey="&securityView.accesskey;" - oncommand="security.viewCert();"/> - </hbox> - </groupbox> - - <!-- Privacy & History section --> - <groupbox id="security-privacy-groupbox" flex="1"> - <caption id="security-privacy" label="&securityView.privacy.header;" /> - <grid id="security-privacy-grid"> - <columns> - <column flex="1"/> - <column flex="1"/> - </columns> - <rows id="security-privacy-rows"> - <!-- History --> - <row id="security-privacy-history-row"> - <label id="security-privacy-history-label" - control="security-privacy-history-value" - class="fieldLabel">&securityView.privacy.history;</label> - <textbox id="security-privacy-history-value" - class="fieldValue" - value="&securityView.unknown;" - readonly="true"/> - </row> - <!-- Cookies --> - <row id="security-privacy-cookies-row"> - <label id="security-privacy-cookies-label" - control="security-privacy-cookies-value" - class="fieldLabel">&securityView.privacy.cookies;</label> - <hbox id="security-privacy-cookies-box" align="center"> - <textbox id="security-privacy-cookies-value" - class="fieldValue" - value="&securityView.unknown;" - flex="1" - readonly="true"/> - <button id="security-view-cookies" - label="&securityView.privacy.viewCookies;" - accesskey="&securityView.privacy.viewCookies.accessKey;" - oncommand="security.viewCookies();"/> - </hbox> - </row> - <!-- Passwords --> - <row id="security-privacy-passwords-row"> - <label id="security-privacy-passwords-label" - control="security-privacy-passwords-value" - class="fieldLabel">&securityView.privacy.passwords;</label> - <hbox id="security-privacy-passwords-box" align="center"> - <textbox id="security-privacy-passwords-value" - class="fieldValue" - value="&securityView.unknown;" - flex="1" - readonly="true"/> - <button id="security-view-password" - label="&securityView.privacy.viewPasswords;" - accesskey="&securityView.privacy.viewPasswords.accessKey;" - oncommand="security.viewPasswords();"/> - </hbox> - </row> - </rows> - </grid> - </groupbox> - - <!-- Technical Details section --> - <groupbox id="security-technical-groupbox" flex="1"> - <caption id="security-technical" label="&securityView.technical.header;" /> - <vbox id="security-technical-box" flex="1"> - <label id="security-technical-shortform" class="fieldValue"/> - <description id="security-technical-longform1" class="fieldLabel"/> - <description id="security-technical-longform2" class="fieldLabel"/> - </vbox> - </groupbox> - </vbox> - <!-- Others added by overlay --> - </deck> - -#ifdef XP_MACOSX -#include ../../base/content/browserMountPoints.inc -#endif - -</window> diff --git a/components/pageinfo/permissions.js b/components/pageinfo/permissions.js deleted file mode 100644 index 4f8382f..0000000 --- a/components/pageinfo/permissions.js +++ /dev/null @@ -1,341 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -const UNKNOWN = nsIPermissionManager.UNKNOWN_ACTION; // 0 -const ALLOW = nsIPermissionManager.ALLOW_ACTION; // 1 -const DENY = nsIPermissionManager.DENY_ACTION; // 2 -const SESSION = nsICookiePermission.ACCESS_SESSION; // 8 - -const IMAGE_DENY = 2; - -const COOKIE_DENY = 2; -const COOKIE_SESSION = 2; - -var gPermURI; -var gPermPrincipal; -var gPrefs; -var gUsageRequest; - -var gPermObj = { - image: function getImageDefaultPermission() - { - if (gPrefs.getIntPref("permissions.default.image") == IMAGE_DENY) { - return DENY; - } - return ALLOW; - }, - popup: function getPopupDefaultPermission() - { - if (gPrefs.getBoolPref("dom.disable_open_during_load")) { - return DENY; - } - return ALLOW; - }, - cookie: function getCookieDefaultPermission() - { - if (gPrefs.getIntPref("network.cookie.cookieBehavior") == COOKIE_DENY) { - return DENY; - } - if (gPrefs.getIntPref("network.cookie.lifetimePolicy") == COOKIE_SESSION) { - return SESSION; - } - return ALLOW; - }, - "desktop-notification": function getNotificationDefaultPermission() - { - if (!gPrefs.getBoolPref("dom.webnotifications.enabled")) { - return DENY; - } - return UNKNOWN; - }, - install: function getInstallDefaultPermission() - { - if (Services.prefs.getBoolPref("xpinstall.whitelist.required")) { - return DENY; - } - return ALLOW; - }, - geo: function getGeoDefaultPermissions() - { - if (!gPrefs.getBoolPref("geo.enabled")) { - return DENY; - } - return ALLOW; - }, - plugins: function getPluginsDefaultPermissions() - { - return UNKNOWN; - }, -}; - -var permissionObserver = { - observe: function (aSubject, aTopic, aData) - { - if (aTopic == "perm-changed") { - var permission = aSubject.QueryInterface( - Components.interfaces.nsIPermission); - if (permission.matchesURI(gPermURI, true)) { - if (permission.type in gPermObj) - initRow(permission.type); - else if (permission.type.startsWith("plugin")) - setPluginsRadioState(); - } - } - } -}; - -function onLoadPermission(principal) -{ - gPrefs = Components.classes[PREFERENCES_CONTRACTID] - .getService(Components.interfaces.nsIPrefBranch); - - var uri = gDocument.documentURIObject; - var permTab = document.getElementById("permTab"); - if (/^https?$/.test(uri.scheme)) { - gPermURI = uri; - gPermPrincipal = principal; - var hostText = document.getElementById("hostText"); - hostText.value = gPermURI.prePath; - - for (var i in gPermObj) - initRow(i); - var os = Components.classes["@mozilla.org/observer-service;1"] - .getService(Components.interfaces.nsIObserverService); - os.addObserver(permissionObserver, "perm-changed", false); - onUnloadRegistry.push(onUnloadPermission); - permTab.hidden = false; - } - else - permTab.hidden = true; -} - -function onUnloadPermission() -{ - var os = Components.classes["@mozilla.org/observer-service;1"] - .getService(Components.interfaces.nsIObserverService); - os.removeObserver(permissionObserver, "perm-changed"); - - if (gUsageRequest) { - gUsageRequest.cancel(); - gUsageRequest = null; - } -} - -function initRow(aPartId) -{ - if (aPartId == "plugins") { - initPluginsRow(); - return; - } - - var permissionManager = Components.classes[PERMISSION_CONTRACTID] - .getService(nsIPermissionManager); - - var checkbox = document.getElementById(aPartId + "Def"); - var command = document.getElementById("cmd_" + aPartId + "Toggle"); - // Desktop Notification, Geolocation and PointerLock permission consumers - // use testExactPermission, not testPermission. - var perm; - if (aPartId == "desktop-notification" || aPartId == "geo" || aPartId == "pointerLock") - perm = permissionManager.testExactPermission(gPermURI, aPartId); - else - perm = permissionManager.testPermission(gPermURI, aPartId); - - if (perm) { - checkbox.checked = false; - command.removeAttribute("disabled"); - } - else { - checkbox.checked = true; - command.setAttribute("disabled", "true"); - perm = gPermObj[aPartId](); - } - setRadioState(aPartId, perm); -} - -function onCheckboxClick(aPartId) -{ - var permissionManager = Components.classes[PERMISSION_CONTRACTID] - .getService(nsIPermissionManager); - - var command = document.getElementById("cmd_" + aPartId + "Toggle"); - var checkbox = document.getElementById(aPartId + "Def"); - if (checkbox.checked) { - permissionManager.remove(gPermURI, aPartId); - command.setAttribute("disabled", "true"); - var perm = gPermObj[aPartId](); - setRadioState(aPartId, perm); - } - else { - onRadioClick(aPartId); - command.removeAttribute("disabled"); - } -} - -function onPluginRadioClick(aEvent) { - onRadioClick(aEvent.originalTarget.getAttribute("id").split('#')[0]); -} - -function onRadioClick(aPartId) -{ - var permissionManager = Components.classes[PERMISSION_CONTRACTID] - .getService(nsIPermissionManager); - - var radioGroup = document.getElementById(aPartId + "RadioGroup"); - var id = radioGroup.selectedItem.id; - var permission = id.split('#')[1]; - if (permission == UNKNOWN) { - permissionManager.remove(gPermURI, aPartId); - } else { - permissionManager.add(gPermURI, aPartId, permission); - } -} - -function setRadioState(aPartId, aValue) -{ - var radio = document.getElementById(aPartId + "#" + aValue); - radio.radioGroup.selectedItem = radio; -} - -// XXX copied this from browser-plugins.js - is there a way to share? -function makeNicePluginName(aName) { - if (aName == "Shockwave Flash") - return "Adobe Flash"; - - // Clean up the plugin name by stripping off any trailing version numbers - // or "plugin". EG, "Foo Bar Plugin 1.23_02" --> "Foo Bar" - // Do this by first stripping the numbers, etc. off the end, and then - // removing "Plugin" (and then trimming to get rid of any whitespace). - // (Otherwise, something like "Java(TM) Plug-in 1.7.0_07" gets mangled) - let newName = aName.replace(/[\s\d\.\-\_\(\)]+$/, "").replace(/\bplug-?in\b/i, "").trim(); - return newName; -} - -function fillInPluginPermissionTemplate(aPermissionString, aPluginObject) { - let permPluginTemplate = document.getElementById("permPluginTemplate") - .cloneNode(true); - permPluginTemplate.setAttribute("permString", aPermissionString); - permPluginTemplate.setAttribute("tooltiptext", aPluginObject.description); - let attrs = []; - attrs.push([".permPluginTemplateLabel", "value", aPluginObject.name]); - attrs.push([".permPluginTemplateRadioGroup", "id", aPermissionString + "RadioGroup"]); - attrs.push([".permPluginTemplateRadioDefault", "id", aPermissionString + "#0"]); - let permPluginTemplateRadioAsk = ".permPluginTemplateRadioAsk"; - if (Services.prefs.getBoolPref("plugins.click_to_play") || - aPluginObject.vulnerable) { - attrs.push([permPluginTemplateRadioAsk, "id", aPermissionString + "#3"]); - } else { - permPluginTemplate.querySelector(permPluginTemplateRadioAsk) - .setAttribute("disabled", "true"); - } - attrs.push([".permPluginTemplateRadioAllow", "id", aPermissionString + "#1"]); - attrs.push([".permPluginTemplateRadioBlock", "id", aPermissionString + "#2"]); - - for (let attr of attrs) { - permPluginTemplate.querySelector(attr[0]).setAttribute(attr[1], attr[2]); - } - - return permPluginTemplate; -} - -function clearPluginPermissionTemplate() { - let permPluginTemplate = document.getElementById("permPluginTemplate"); - permPluginTemplate.hidden = true; - permPluginTemplate.removeAttribute("permString"); - permPluginTemplate.removeAttribute("tooltiptext"); - document.querySelector(".permPluginTemplateLabel").removeAttribute("value"); - document.querySelector(".permPluginTemplateRadioGroup").removeAttribute("id"); - document.querySelector(".permPluginTemplateRadioAsk").removeAttribute("id"); - document.querySelector(".permPluginTemplateRadioAllow").removeAttribute("id"); - document.querySelector(".permPluginTemplateRadioBlock").removeAttribute("id"); -} - -function initPluginsRow() { - let vulnerableLabel = document.getElementById("browserBundle") - .getString("pluginActivateVulnerable.label"); - let pluginHost = Components.classes["@mozilla.org/plugin/host;1"] - .getService(Components.interfaces.nsIPluginHost); - let tags = pluginHost.getPluginTags(); - - let permissionMap = new Map(); - - for (let plugin of tags) { - if (plugin.disabled) { - continue; - } - for (let mimeType of plugin.getMimeTypes()) { - if (mimeType == "application/x-shockwave-flash" && plugin.name != "Shockwave Flash") { - continue; - } - let permString = pluginHost.getPermissionStringForType(mimeType); - if (!permissionMap.has(permString)) { - let name = makeNicePluginName(plugin.name) + " " + plugin.version; - let vulnerable = false; - if (permString.startsWith("plugin-vulnerable:")) { - name += " \u2014 " + vulnerableLabel; - vulnerable = true; - } - permissionMap.set(permString, { - "name": name, - "description": plugin.description, - "vulnerable": vulnerable - }); - } - } - } - - // Tycho: - // let entries = [ - // { - // "permission": item[0], - // "obj": item[1], - // } - // for (item of permissionMap) - // ]; - let entries = []; - for (let item of permissionMap) { - entries.push({ - "permission": item[0], - "obj": item[1] - }); - } - entries.sort(function(a, b) { - return ((a.obj.name < b.obj.name) ? -1 : (a.obj.name == b.obj.name ? 0 : 1)); - }); - - // Tycho: - // let permissionEntries = [ - // fillInPluginPermissionTemplate(p.permission, p.obj) for (p of entries) - // ]; - let permissionEntries = []; - entries.forEach(function (p) { - permissionEntries.push(fillInPluginPermissionTemplate(p.permission, p.obj)); - }); - - let permPluginsRow = document.getElementById("permPluginsRow"); - clearPluginPermissionTemplate(); - if (permissionEntries.length < 1) { - permPluginsRow.hidden = true; - return; - } - - for (let permissionEntry of permissionEntries) { - permPluginsRow.appendChild(permissionEntry); - } - - setPluginsRadioState(); -} - -function setPluginsRadioState() { - var permissionManager = Components.classes[PERMISSION_CONTRACTID] - .getService(nsIPermissionManager); - let box = document.getElementById("permPluginsRow"); - for (let permissionEntry of box.childNodes) { - if (permissionEntry.hasAttribute("permString")) { - let permString = permissionEntry.getAttribute("permString"); - let permission = permissionManager.testPermission(gPermURI, permString); - setRadioState(permString, permission); - } - } -} diff --git a/components/pageinfo/security.js b/components/pageinfo/security.js deleted file mode 100644 index e791ab9..0000000 --- a/components/pageinfo/security.js +++ /dev/null @@ -1,378 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var security = { - // Display the server certificate (static) - viewCert : function () { - var cert = security._cert; - viewCertHelper(window, cert); - }, - - _getSecurityInfo : function() { - const nsIX509Cert = Components.interfaces.nsIX509Cert; - const nsIX509CertDB = Components.interfaces.nsIX509CertDB; - const nsX509CertDB = "@mozilla.org/security/x509certdb;1"; - const nsISSLStatusProvider = Components.interfaces.nsISSLStatusProvider; - const nsISSLStatus = Components.interfaces.nsISSLStatus; - - // We don't have separate info for a frame, return null until further notice - // (see bug 138479) - if (gWindow != gWindow.top) - return null; - - var hName = null; - try { - hName = gWindow.location.host; - } - catch (exception) { } - - var ui = security._getSecurityUI(); - if (!ui) - return null; - - var isBroken = - (ui.state & Components.interfaces.nsIWebProgressListener.STATE_IS_BROKEN); - var isMixed = - (ui.state & (Components.interfaces.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT | - Components.interfaces.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT)); - var isInsecure = - (ui.state & Components.interfaces.nsIWebProgressListener.STATE_IS_INSECURE); - var isEV = - (ui.state & Components.interfaces.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL); - ui.QueryInterface(nsISSLStatusProvider); - var status = ui.SSLStatus; - - if (!isInsecure && status) { - status.QueryInterface(nsISSLStatus); - var cert = status.serverCert; - var issuerName = - this.mapIssuerOrganization(cert.issuerOrganization) || cert.issuerName; - - var retval = { - hostName : hName, - cAName : issuerName, - encryptionAlgorithm : undefined, - encryptionStrength : undefined, - encryptionSuite : undefined, - version: undefined, - isBroken : isBroken, - isMixed : isMixed, - isEV : isEV, - cert : cert, - fullLocation : gWindow.location - }; - - var version; - try { - retval.encryptionAlgorithm = status.cipherName; - retval.encryptionStrength = status.secretKeyLength; - retval.encryptionSuite = status.cipherSuite; - version = status.protocolVersion; - } - catch (e) { - } - - switch (version) { - case nsISSLStatus.SSL_VERSION_3: - retval.version = "SSL 3"; - break; - case nsISSLStatus.TLS_VERSION_1: - retval.version = "TLS 1.0"; - break; - case nsISSLStatus.TLS_VERSION_1_1: - retval.version = "TLS 1.1"; - break; - case nsISSLStatus.TLS_VERSION_1_2: - retval.version = "TLS 1.2" - break; - case nsISSLStatus.TLS_VERSION_1_3: - retval.version = "TLS 1.3" - break; - } - - return retval; - } else { - return { - hostName : hName, - cAName : "", - encryptionAlgorithm : "", - encryptionStrength : 0, - encryptionSuite : "", - version: "", - isBroken : isBroken, - isMixed : isMixed, - isEV : isEV, - cert : null, - fullLocation : gWindow.location - }; - } - }, - - // Find the secureBrowserUI object (if present) - _getSecurityUI : function() { - if (window.opener.gBrowser) - return window.opener.gBrowser.securityUI; - return null; - }, - - // Interface for mapping a certificate issuer organization to - // the value to be displayed. - // Bug 82017 - this implementation should be moved to pipnss C++ code - mapIssuerOrganization: function(name) { - if (!name) return null; - - if (name == "RSA Data Security, Inc.") return "Verisign, Inc."; - - // No mapping required - return name; - }, - - /** - * Open the cookie manager window - */ - viewCookies : function() - { - var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] - .getService(Components.interfaces.nsIWindowMediator); - var win = wm.getMostRecentWindow("Browser:Cookies"); - var eTLDService = Components.classes["@mozilla.org/network/effective-tld-service;1"]. - getService(Components.interfaces.nsIEffectiveTLDService); - - var eTLD; - var uri = gDocument.documentURIObject; - try { - eTLD = eTLDService.getBaseDomain(uri); - } - catch (e) { - // getBaseDomain will fail if the host is an IP address or is empty - eTLD = uri.asciiHost; - } - - if (win) { - win.gCookiesWindow.setFilter(eTLD); - win.focus(); - } - else - window.openDialog("chrome://browser/content/preferences/cookies.xul", - "Browser:Cookies", "", {filterString : eTLD}); - }, - - /** - * Open the login manager window - */ - viewPasswords : function() - { - var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] - .getService(Components.interfaces.nsIWindowMediator); - var win = wm.getMostRecentWindow("Toolkit:PasswordManager"); - if (win) { - win.setFilter(this._getSecurityInfo().hostName); - win.focus(); - } - else - window.openDialog("chrome://passwordmgr/content/passwordManager.xul", - "Toolkit:PasswordManager", "", - {filterString : this._getSecurityInfo().hostName}); - }, - - _cert : null -}; - -function securityOnLoad() { - var info = security._getSecurityInfo(); - if (!info) { - document.getElementById("securityTab").hidden = true; - document.getElementById("securityBox").collapsed = true; - return; - } - else { - document.getElementById("securityTab").hidden = false; - document.getElementById("securityBox").collapsed = false; - } - - const pageInfoBundle = document.getElementById("pageinfobundle"); - - /* Set Identity section text */ - setText("security-identity-domain-value", info.hostName); - - var owner, verifier, generalPageIdentityString; - if (info.cert && !info.isBroken) { - // Try to pull out meaningful values. Technically these fields are optional - // so we'll employ fallbacks where appropriate. The EV spec states that Org - // fields must be specified for subject and issuer so that case is simpler. - if (info.isEV) { - owner = info.cert.organization; - verifier = security.mapIssuerOrganization(info.cAName); - generalPageIdentityString = pageInfoBundle.getFormattedString("generalSiteIdentity", - [owner, verifier]); - } - else { - // Technically, a non-EV cert might specify an owner in the O field or not, - // depending on the CA's issuing policies. However we don't have any programmatic - // way to tell those apart, and no policy way to establish which organization - // vetting standards are good enough (that's what EV is for) so we default to - // treating these certs as domain-validated only. - owner = pageInfoBundle.getString("securityNoOwner"); - verifier = security.mapIssuerOrganization(info.cAName || - info.cert.issuerCommonName || - info.cert.issuerName); - generalPageIdentityString = owner; - } - } - else { - // We don't have valid identity credentials. - owner = pageInfoBundle.getString("securityNoOwner"); - verifier = pageInfoBundle.getString("notset"); - generalPageIdentityString = owner; - } - - setText("security-identity-owner-value", owner); - setText("security-identity-verifier-value", verifier); - setText("general-security-identity", generalPageIdentityString); - - /* Manage the View Cert button*/ - var viewCert = document.getElementById("security-view-cert"); - if (info.cert) { - security._cert = info.cert; - viewCert.collapsed = false; - } - else - viewCert.collapsed = true; - - /* Set Privacy & History section text */ - var yesStr = pageInfoBundle.getString("yes"); - var noStr = pageInfoBundle.getString("no"); - - var uri = gDocument.documentURIObject; - setText("security-privacy-cookies-value", - hostHasCookies(uri) ? yesStr : noStr); - setText("security-privacy-passwords-value", - realmHasPasswords(uri) ? yesStr : noStr); - - var visitCount = previousVisitCount(info.hostName); - if(visitCount > 1) { - setText("security-privacy-history-value", - pageInfoBundle.getFormattedString("securityNVisits", [visitCount.toLocaleString()])); - } - else if (visitCount == 1) { - setText("security-privacy-history-value", - pageInfoBundle.getString("securityOneVisit")); - } - else { - setText("security-privacy-history-value", noStr); - } - - /* Set the Technical Detail section messages */ - const pkiBundle = document.getElementById("pkiBundle"); - var hdr; - var msg1; - var msg2; - - if (info.isBroken) { - if (info.isMixed) { - hdr = pkiBundle.getString("pageInfo_MixedContent"); - } else { - hdr = pkiBundle.getFormattedString("pageInfo_BrokenEncryption", - [info.encryptionAlgorithm, - info.encryptionStrength + "", - info.version]); - } - msg1 = pkiBundle.getString("pageInfo_Privacy_Broken1"); - msg2 = pkiBundle.getString("pageInfo_Privacy_None2"); - } - else if (info.encryptionStrength > 0) { - hdr = pkiBundle.getFormattedString("pageInfo_EncryptionWithBitsAndProtocol", - [info.encryptionAlgorithm, - info.encryptionStrength + "", - info.version]); - msg1 = pkiBundle.getString("pageInfo_Privacy_Encrypted1"); - msg2 = pkiBundle.getString("pageInfo_Privacy_Encrypted2"); - security._cert = info.cert; - } - else { - hdr = pkiBundle.getString("pageInfo_NoEncryption"); - if (info.hostName != null) - msg1 = pkiBundle.getFormattedString("pageInfo_Privacy_None1", [info.hostName]); - else - msg1 = pkiBundle.getString("pageInfo_Privacy_None3"); - msg2 = pkiBundle.getString("pageInfo_Privacy_None2"); - } - setText("security-technical-shortform", hdr); - setText("security-technical-longform1", msg1); - setText("security-technical-longform2", msg2); - setText("general-security-privacy", hdr); -} - -function setText(id, value) -{ - var element = document.getElementById(id); - if (!element) - return; - if (element.localName == "textbox" || element.localName == "label") - element.value = value; - else { - if (element.hasChildNodes()) - element.removeChild(element.firstChild); - var textNode = document.createTextNode(value); - element.appendChild(textNode); - } -} - -function viewCertHelper(parent, cert) -{ - if (!cert) - return; - - var cd = Components.classes[CERTIFICATEDIALOGS_CONTRACTID].getService(nsICertificateDialogs); - cd.viewCert(parent, cert); -} - -/** - * Return true iff we have cookies for uri - */ -function hostHasCookies(uri) { - var cookieManager = Components.classes["@mozilla.org/cookiemanager;1"] - .getService(Components.interfaces.nsICookieManager2); - - return cookieManager.countCookiesFromHost(uri.asciiHost) > 0; -} - -/** - * Return true iff realm (proto://host:port) (extracted from uri) has - * saved passwords - */ -function realmHasPasswords(uri) { - var passwordManager = Components.classes["@mozilla.org/login-manager;1"] - .getService(Components.interfaces.nsILoginManager); - return passwordManager.countLogins(uri.prePath, "", "") > 0; -} - -/** - * Return the number of previous visits recorded for host before today. - * - * @param host - the domain name to look for in history - */ -function previousVisitCount(host, endTimeReference) { - if (!host) - return false; - - var historyService = Components.classes["@mozilla.org/browser/nav-history-service;1"] - .getService(Components.interfaces.nsINavHistoryService); - - var options = historyService.getNewQueryOptions(); - options.resultType = options.RESULTS_AS_VISIT; - - // Search for visits to this host before today - var query = historyService.getNewQuery(); - query.endTimeReference = query.TIME_RELATIVE_TODAY; - query.endTime = 0; - query.domain = host; - - var result = historyService.executeQuery(query, options); - result.root.containerOpen = true; - var cc = result.root.childCount; - result.root.containerOpen = false; - return cc; -} diff --git a/components/permissions/aboutPermissions.css b/components/permissions/aboutPermissions.css deleted file mode 100644 index d73b6a8..0000000 --- a/components/permissions/aboutPermissions.css +++ /dev/null @@ -1,11 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -.site { - -moz-binding: url("chrome://browser/content/permissions/aboutPermissions.xml#site"); -} - -.pluginPermission { - -moz-binding: url("chrome://browser/content/permissions/aboutPermissions.xml#pluginPermission"); -} diff --git a/components/permissions/aboutPermissions.js b/components/permissions/aboutPermissions.js deleted file mode 100644 index 421b65a..0000000 --- a/components/permissions/aboutPermissions.js +++ /dev/null @@ -1,1335 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -var Ci = Components.interfaces; -var Cc = Components.classes; -var Cu = Components.utils; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/DownloadUtils.jsm"); -Cu.import("resource://gre/modules/AddonManager.jsm"); -Cu.import("resource://gre/modules/NetUtil.jsm"); -Cu.import("resource://gre/modules/ForgetAboutSite.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "PluralForm", - "resource://gre/modules/PluralForm.jsm"); - -var gSecMan = Cc["@mozilla.org/scriptsecuritymanager;1"]. - getService(Ci.nsIScriptSecurityManager); - -var gFaviconService = Cc["@mozilla.org/browser/favicon-service;1"]. - getService(Ci.nsIFaviconService); - -var gPlacesDatabase = Cc["@mozilla.org/browser/nav-history-service;1"]. - getService(Ci.nsPIPlacesDatabase). - DBConnection. - clone(true); - -var gSitesStmt = gPlacesDatabase.createAsyncStatement( - "SELECT url " + - "FROM moz_places " + - "WHERE rev_host > '.' " + - "AND visit_count > 0 " + - "GROUP BY rev_host " + - "ORDER BY MAX(frecency) DESC " + - "LIMIT :limit"); - -var gVisitStmt = gPlacesDatabase.createAsyncStatement( - "SELECT SUM(visit_count) AS count " + - "FROM moz_places " + - "WHERE rev_host = :rev_host"); - -var gFlash = { - name: "Shockwave Flash", - betterName: "Adobe Flash", - type: "application/x-shockwave-flash", -}; - -// XXX: -// Is there a better way to do this rather than this hacky comparison? -// Copied this from toolkit/components/passwordmgr/crypto-SDR.js -const MASTER_PASSWORD_MESSAGE = "User canceled master password entry"; - -/** - * Permission types that should be tested with testExactPermission, as opposed - * to testPermission. This is based on what consumers use to test these - * permissions. - */ -const TEST_EXACT_PERM_TYPES = ["desktop-notification", "geo", "pointerLock"]; - -/** - * Site object represents a single site, uniquely identified by a principal. - */ -function Site(principal) { - this.principal = principal; - this.listitem = null; -} - -Site.prototype = { - /** - * Gets the favicon to use for the site. The callback only gets called if - * a favicon is found for either the http URI or the https URI. - * - * @param aCallback - * A callback function that takes a favicon image URL as a parameter. - */ - getFavicon: function(aCallback) { - function invokeCallback(aFaviconURI) { - try { - // Use getFaviconLinkForIcon to get image data from the database instead - // of using the favicon URI to fetch image data over the network. - aCallback(gFaviconService.getFaviconLinkForIcon(aFaviconURI).spec); - } catch (e) { - Cu.reportError("AboutPermissions: " + e); - } - } - - // Get the favicon for the origin - gFaviconService.getFaviconURLForPage(this.principal.URI, function (aURI) { - if (aURI) { - invokeCallback(aURI); - } - }.bind(this)); - }, - - /** - * Gets the number of history visits for the site. - * - * @param aCallback - * A function that takes the visit count (a number) as a parameter. - */ - getVisitCount: function(aCallback) { - // XXX This won't be a very reliable system, as it will count both http: and https: visits - // Unfortunately, I don't think that there is a much better way to do it right now. - let rev_host = this.principal.URI.host.split("").reverse().join("") + "."; - gVisitStmt.params.rev_host = rev_host; - gVisitStmt.executeAsync({ - handleResult: function(aResults) { - let row = aResults.getNextRow(); - let count = row.getResultByName("count") || 0; - try { - aCallback(count); - } catch (e) { - Cu.reportError("AboutPermissions: " + e); - } - }, - handleError: function(aError) { - Cu.reportError("AboutPermissions: " + aError); - }, - handleCompletion: function(aReason) { - } - }); - }, - - /** - * Gets the permission value stored for a specified permission type. - * - * @param aType - * The permission type string stored in permission manager. - * e.g. "cookie", "geo", "popup", "image" - * @param aResultObj - * An object that stores the permission value set for aType. - * - * @return A boolean indicating whether or not a permission is set. - */ - getPermission: function(aType, aResultObj) { - // Password saving isn't a nsIPermissionManager permission type, so handle - // it seperately. - if (aType == "password") { - aResultObj.value = this.loginSavingEnabled - ? Ci.nsIPermissionManager.ALLOW_ACTION - : Ci.nsIPermissionManager.DENY_ACTION; - return true; - } - - let permissionValue; - if (TEST_EXACT_PERM_TYPES.indexOf(aType) == -1) { - permissionValue = Services.perms.testPermissionFromPrincipal(this.principal, aType); - } else { - permissionValue = Services.perms.testExactPermissionFromPrincipal(this.principal, aType); - } - aResultObj.value = permissionValue; - - if (aType.startsWith("plugin")) { - if (permissionValue == Ci.nsIPermissionManager.PROMPT_ACTION) { - aResultObj.value = Ci.nsIPermissionManager.UNKNOWN_ACTION; - return true; - } - } - - return permissionValue != Ci.nsIPermissionManager.UNKNOWN_ACTION; - }, - - /** - * Sets a permission for the site given a permission type and value. - * - * @param aType - * The permission type string stored in permission manager. - * e.g. "cookie", "geo", "popup", "image" - * @param aPerm - * The permission value to set for the permission type. This should - * be one of the constants defined in nsIPermissionManager. - */ - setPermission: function(aType, aPerm) { - // Password saving isn't a nsIPermissionManager permission type, so handle - // it seperately. - if (aType == "password") { - this.loginSavingEnabled = aPerm == Ci.nsIPermissionManager.ALLOW_ACTION; - return; - } - - if (aType.startsWith("plugin")) { - if (aPerm == Ci.nsIPermissionManager.UNKNOWN_ACTION) { - aPerm = Ci.nsIPermissionManager.PROMPT_ACTION; - } - } - - Services.perms.addFromPrincipal(this.principal, aType, aPerm); - }, - - /** - * Clears a user-set permission value for the site given a permission type. - * - * @param aType - * The permission type string stored in permission manager. - * e.g. "cookie", "geo", "popup", "image" - */ - clearPermission: function(aType) { - Services.perms.removeFromPrincipal(this.principal, aType); - }, - - /** - * Gets logins stored for the site. - * - * @return An array of the logins stored for the site. - */ - get logins() { - try { - let logins = Services.logins.findLogins({}, - this.principal.originNoSuffix, "", ""); - return logins; - } catch (e) { - if (!e.message.includes(MASTER_PASSWORD_MESSAGE)) { - Cu.reportError("AboutPermissions: " + e); - } - return []; - } - }, - - get loginSavingEnabled() { - // Only say that login saving is blocked if it is blocked for both - // http and https. - try { - return Services.logins.getLoginSavingEnabled(this.principal.originNoSuffix); - } catch (e) { - if (!e.message.includes(MASTER_PASSWORD_MESSAGE)) { - Cu.reportError("AboutPermissions: " + e); - } - return false; - } - }, - - set loginSavingEnabled(isEnabled) { - try { - Services.logins.setLoginSavingEnabled(this.principal.originNoSuffix, isEnabled); - } catch (e) { - if (!e.message.includes(MASTER_PASSWORD_MESSAGE)) { - Cu.reportError("AboutPermissions: " + e); - } - } - }, - - /** - * Gets cookies stored for the site and base domain. - * - * @return An array of the cookies set for the site and base domain. - */ - get cookies() { - let cookies = []; - let enumerator = Services.cookies.enumerator; - while (enumerator.hasMoreElements()) { - let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2); - if (cookie.host.hasRootDomain( - AboutPermissions.domainFromHost(this.principal.URI.host))) { - cookies.push(cookie); - } - } - return cookies; - }, - - /** - * Removes a set of specific cookies from the browser. - */ - clearCookies: function() { - this.cookies.forEach(function(aCookie) { - Services.cookies.remove(aCookie.host, aCookie.name, aCookie.path, false, - aCookie.originAttributes); - }); - }, - - /** - * Removes all data from the browser corresponding to the site. - */ - forgetSite: function() { - // XXX This removes data for an entire domain, rather than just - // an origin. This may produce confusing results, as data will - // be cleared for the http:// as well as the https:// domain - // if you try to forget the https:// site. - ForgetAboutSite.removeDataFromDomain(this.principal.URI.host) - .catch(Cu.reportError); - } -} - -/** - * PermissionDefaults object keeps track of default permissions for sites based - * on global preferences. - * - * Inspired by pageinfo/permissions.js - */ -var PermissionDefaults = { - UNKNOWN: Ci.nsIPermissionManager.UNKNOWN_ACTION, // 0 - ALLOW: Ci.nsIPermissionManager.ALLOW_ACTION, // 1 - DENY: Ci.nsIPermissionManager.DENY_ACTION, // 2 - SESSION: Ci.nsICookiePermission.ACCESS_SESSION, // 8 - - get password() { - if (Services.prefs.getBoolPref("signon.rememberSignons")) { - return this.ALLOW; - } - return this.DENY; - }, - set password(aValue) { - let value = (aValue != this.DENY); - Services.prefs.setBoolPref("signon.rememberSignons", value); - }, - - IMAGE_ALLOW: 1, - IMAGE_DENY: 2, - IMAGE_ALLOW_FIRST_PARTY_ONLY: 3, - - get image() { - if (Services.prefs.getIntPref("permissions.default.image") - == this.IMAGE_DENY) { - return this.IMAGE_DENY; - } else if (Services.prefs.getIntPref("permissions.default.image") - == this.IMAGE_ALLOW_FIRST_PARTY_ONLY) { - return this.IMAGE_ALLOW_FIRST_PARTY_ONLY; - } - return this.IMAGE_ALLOW; - }, - set image(aValue) { - let value = this.IMAGE_ALLOW; - if (aValue == this.IMAGE_DENY) { - value = this.IMAGE_DENY; - } else if (aValue == this.IMAGE_ALLOW_FIRST_PARTY_ONLY) { - value = this.IMAGE_ALLOW_FIRST_PARTY_ONLY; - } - Services.prefs.setIntPref("permissions.default.image", value); - }, - - get popup() { - if (Services.prefs.getBoolPref("dom.disable_open_during_load")) { - return this.DENY; - } - return this.ALLOW; - }, - set popup(aValue) { - let value = (aValue == this.DENY); - Services.prefs.setBoolPref("dom.disable_open_during_load", value); - }, - - // For use with network.cookie.* prefs. - COOKIE_ACCEPT: 0, - COOKIE_DENY: 2, - COOKIE_NORMAL: 0, - COOKIE_SESSION: 2, - - get cookie() { - if (Services.prefs.getIntPref("network.cookie.cookieBehavior") - == this.COOKIE_DENY) { - return this.DENY; - } - - if (Services.prefs.getIntPref("network.cookie.lifetimePolicy") - == this.COOKIE_SESSION) { - return this.SESSION; - } - return this.ALLOW; - }, - set cookie(aValue) { - let value = (aValue == this.DENY) ? this.COOKIE_DENY : this.COOKIE_ACCEPT; - Services.prefs.setIntPref("network.cookie.cookieBehavior", value); - - let lifetimeValue = aValue == this.SESSION ? this.COOKIE_SESSION : - this.COOKIE_NORMAL; - Services.prefs.setIntPref("network.cookie.lifetimePolicy", lifetimeValue); - }, - - get ["desktop-notification"]() { - if (!Services.prefs.getBoolPref("dom.webnotifications.enabled")) { - return this.DENY; - } - // We always ask for permission to enable notifications for a specific - // site, so there is no global ALLOW. - return this.UNKNOWN; - }, - set ["desktop-notification"](aValue) { - let value = (aValue != this.DENY); - Services.prefs.setBoolPref("dom.webnotifications.enabled", value); - }, - - get install() { - if (Services.prefs.getBoolPref("xpinstall.whitelist.required")) { - return this.DENY; - } - return this.ALLOW; - }, - set install(aValue) { - let value = (aValue == this.DENY); - Services.prefs.setBoolPref("xpinstall.whitelist.required", value); - }, - - get geo() { - if (!Services.prefs.getBoolPref("geo.enabled")) { - return this.DENY; - } - // We always ask for permission to share location with a specific site, - // so there is no global ALLOW. - return this.UNKNOWN; - }, - set geo(aValue) { - let value = (aValue != this.DENY); - Services.prefs.setBoolPref("geo.enabled", value); - }, -} - -/** - * AboutPermissions manages the about:permissions page. - */ -var AboutPermissions = { - /** - * Maximum number of sites to return from the places database. - */ - PLACES_SITES_LIMIT_MAX: 100, - - /** - * When adding sites to the dom sites-list, divide workload into intervals. - */ - LIST_BUILD_DELAY: 100, // delay between intervals - - /** - * Stores a mapping of origin strings to Site objects. - */ - _sites: {}, - - /** - * Using a getter for sitesFilter to avoid races with tests. - */ - get sitesFilter () { - delete this.sitesFilter; - return this.sitesFilter = document.getElementById("sites-filter"); - }, - - sitesList: null, - _selectedSite: null, - - /** - * For testing, track initializations so we can send notifications. - */ - _initPlacesDone: false, - _initServicesDone: false, - - /** - * This reflects the permissions that we expose in the UI. These correspond - * to permission type strings in the permission manager, PermissionDefaults, - * and element ids in aboutPermissions.xul. - * - * Potential future additions: "sts/use", "sts/subd" - */ - _supportedPermissions: ["password", "image", "popup", "cookie", - "desktop-notification", "install", "geo"], - - /** - * Permissions that don't have a global "Allow" option. - */ - _noGlobalAllow: ["desktop-notification", "geo"], - - /** - * Permissions that don't have a global "Deny" option. - */ - _noGlobalDeny: [], - - _stringBundleBrowser: Services.strings - .createBundle("chrome://browser/locale/browser.properties"), - - _stringBundleAboutPermissions: Services.strings.createBundle( - "chrome://browser/locale/permissions/aboutPermissions.properties"), - - _initPart1: function() { - this.initPluginList(); - this.cleanupPluginList(); - - this.getSitesFromPlaces(); - - this.enumerateServicesGenerator = this.getEnumerateServicesGenerator(); - setTimeout(this.enumerateServicesDriver.bind(this), this.LIST_BUILD_DELAY); - }, - - _initPart2: function() { - this._supportedPermissions.forEach(function(aType) { - this.updatePermission(aType); - }, this); - }, - - /** - * Called on page load. - */ - init: function() { - this.sitesList = document.getElementById("sites-list"); - - this._initPart1(); - - // Attach observers in case data changes while the page is open. - Services.prefs.addObserver("signon.rememberSignons", this, false); - Services.prefs.addObserver("permissions.default.image", this, false); - Services.prefs.addObserver("dom.disable_open_during_load", this, false); - Services.prefs.addObserver("network.cookie.", this, false); - Services.prefs.addObserver("dom.webnotifications.enabled", this, false); - Services.prefs.addObserver("xpinstall.whitelist.required", this, false); - Services.prefs.addObserver("geo.enabled", this, false); - Services.prefs.addObserver("plugins.click_to_play", this, false); - Services.prefs.addObserver("permissions.places-sites-limit", this, false); - - Services.obs.addObserver(this, "perm-changed", false); - Services.obs.addObserver(this, "passwordmgr-storage-changed", false); - Services.obs.addObserver(this, "cookie-changed", false); - Services.obs.addObserver(this, "browser:purge-domain-data", false); - Services.obs.addObserver(this, "plugin-info-updated", false); - Services.obs.addObserver(this, "plugin-list-updated", false); - Services.obs.addObserver(this, "blocklist-updated", false); - - this._observersInitialized = true; - Services.obs.notifyObservers(null, "browser-permissions-preinit", null); - - this._initPart2(); - - // Process about:permissions?filter=<string> - // About URIs don't support query params, so do this manually - var loc = document.location.href; - var matches = /[?&]filter\=([^&]+)/i.exec(loc); - if (matches) { - this.sitesFilter.value = decodeURIComponent(matches[1]); - } - }, - - sitesReload: function() { - Object.getOwnPropertyNames(this._sites).forEach(function(prop) { - AboutPermissions.deleteFromSitesList(prop); - }); - this._initPart1(); - this._initPart2(); - }, - - // XXX copied this from browser-plugins.js - is there a way to share? - // Map the plugin's name to a filtered version more suitable for user UI. - makeNicePluginName: function(aName) { - if (aName == gFlash.name) { - return gFlash.betterName; - } - - // Clean up the plugin name by stripping off any trailing version numbers - // or "plugin". EG, "Foo Bar Plugin 1.23_02" --> "Foo Bar" - // Do this by first stripping the numbers, etc. off the end, and then - // removing "Plugin" (and then trimming to get rid of any whitespace). - // (Otherwise, something like "Java(TM) Plug-in 1.7.0_07" gets mangled.) - let newName = aName.replace( - /[\s\d\.\-\_\(\)]+$/, "").replace(/\bplug-?in\b/i, "").trim(); - return newName; - }, - - initPluginList: function() { - let pluginHost = Cc["@mozilla.org/plugin/host;1"] - .getService(Ci.nsIPluginHost); - let tags = pluginHost.getPluginTags(); - - let permissionMap = new Map(); - - let permissionEntries = []; - let XUL_NS = - "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; - for (let plugin of tags) { - for (let mimeType of plugin.getMimeTypes()) { - if ((mimeType == gFlash.type) && (plugin.name != gFlash.name)) { - continue; - } - let permString = pluginHost.getPermissionStringForType(mimeType); - if (!permissionMap.has(permString)) { - let permissionEntry = document.createElementNS(XUL_NS, "box"); - permissionEntry.setAttribute("label", - this.makeNicePluginName(plugin.name) - + " " + plugin.version); - permissionEntry.setAttribute("tooltiptext", plugin.description); - permissionEntry.setAttribute("vulnerable", ""); - permissionEntry.setAttribute("mimeType", mimeType); - permissionEntry.setAttribute("permString", permString); - permissionEntry.setAttribute("class", "pluginPermission"); - permissionEntry.setAttribute("id", permString + "-entry"); - // If the plugin is disabled, it makes no sense to change its - // click-to-play status, so don't add it. - if (plugin.disabled) { - permissionEntry.hidden = true; - } else { - permissionEntry.hidden = false; - } - permissionEntries.push(permissionEntry); - this._supportedPermissions.push(permString); - this._noGlobalDeny.push(permString); - Object.defineProperty(PermissionDefaults, permString, { - get: function() { - if ((Services.prefs.getBoolPref("plugins.click_to_play") && - plugin.clicktoplay) || - permString.startsWith("plugin-vulnerable:")) { - return PermissionDefaults.UNKNOWN; - } - return PermissionDefaults.ALLOW; - }, - set: function(aValue) { - this.clicktoplay = (aValue == PermissionDefaults.UNKNOWN); - }.bind(plugin), - configurable: true - }); - permissionMap.set(permString, ""); - } - } - } - - if (permissionEntries.length > 0) { - permissionEntries.sort(function(entryA, entryB) { - let labelA = entryA.getAttribute("label"); - let labelB = entryB.getAttribute("label"); - return ((labelA < labelB) ? -1 : (labelA == labelB ? 0 : 1)); - }); - } - - let pluginsBox = document.getElementById("plugins-box"); - while (pluginsBox.hasChildNodes()) { - pluginsBox.removeChild(pluginsBox.firstChild); - } - for (let permissionEntry of permissionEntries) { - pluginsBox.appendChild(permissionEntry); - } - }, - - cleanupPluginList: function() { - let pluginsPrefItem = document.getElementById("plugins-pref-item"); - let pluginsBox = document.getElementById("plugins-box"); - let pluginsBoxEmpty = true; - let pluginsBoxSibling = pluginsBox.firstChild; - while (pluginsBoxSibling) { - if (!pluginsBoxSibling.hidden) { - pluginsBoxEmpty = false; - break; - } - pluginsBoxSibling = pluginsBoxSibling.nextSibling; - } - if (pluginsBoxEmpty) { - pluginsPrefItem.collapsed = true; - } else { - pluginsPrefItem.collapsed = false; - } - }, - - /** - * Called on page unload. - */ - cleanUp: function() { - if (this._observersInitialized) { - Services.prefs.removeObserver("signon.rememberSignons", this, false); - Services.prefs.removeObserver("permissions.default.image", this, false); - Services.prefs.removeObserver("dom.disable_open_during_load", this, false); - Services.prefs.removeObserver("network.cookie.", this, false); - Services.prefs.removeObserver("dom.webnotifications.enabled", this, false); - Services.prefs.removeObserver("xpinstall.whitelist.required", this, false); - Services.prefs.removeObserver("geo.enabled", this, false); - Services.prefs.removeObserver("plugins.click_to_play", this, false); - Services.prefs.removeObserver("permissions.places-sites-limit", this, false); - - Services.obs.removeObserver(this, "perm-changed"); - Services.obs.removeObserver(this, "passwordmgr-storage-changed"); - Services.obs.removeObserver(this, "cookie-changed"); - Services.obs.removeObserver(this, "browser:purge-domain-data"); - Services.obs.removeObserver(this, "plugin-info-updated"); - Services.obs.removeObserver(this, "plugin-list-updated"); - Services.obs.removeObserver(this, "blocklist-updated"); - } - - gSitesStmt.finalize(); - gVisitStmt.finalize(); - gPlacesDatabase.asyncClose(null); - }, - - observe: function(aSubject, aTopic, aData) { - switch(aTopic) { - case "perm-changed": - // Permissions changes only affect individual sites. - if (!this._selectedSite) { - break; - } - // aSubject is null when nsIPermisionManager::removeAll() is called. - if (!aSubject) { - this._supportedPermissions.forEach(function(aType) { - this.updatePermission(aType); - }, this); - break; - } - let permission = aSubject.QueryInterface(Ci.nsIPermission); - // We can't compare selectedSite.principal and permission.principal here - // because we need to handle the case where a parent domain was changed - // in a way that affects the subdomain. - if (this._supportedPermissions.indexOf(permission.type) != -1) { - this.updatePermission(permission.type); - } - break; - case "nsPref:changed": - if (aData == "permissions.places-sites-limit") { - this.sitesReload(); - return; - } - let plugin = false; - if (aData.startsWith("plugin")) { - plugin = true; - } - if (plugin) { - this.initPluginList(); - } - this._supportedPermissions.forEach(function(aType) { - if (!plugin || (plugin && aType.startsWith("plugin"))) { - this.updatePermission(aType); - } - }, this); - if (plugin) { - this.cleanupPluginList(); - } - break; - case "passwordmgr-storage-changed": - this.updatePermission("password"); - if (this._selectedSite) { - this.updatePasswordsCount(); - } - break; - case "cookie-changed": - if (this._selectedSite) { - this.updateCookiesCount(); - } - break; - case "browser:purge-domain-data": - this.deleteFromSitesList(aData); - break; - case "plugin-info-updated": - case "plugin-list-updated": - case "blocklist-updated": - this.initPluginList(); - this._supportedPermissions.forEach(function(aType) { - if (aType.startsWith("plugin")) { - this.updatePermission(aType); - } - }, this); - this.cleanupPluginList(); - break; - } - }, - - /** - * Creates Site objects for the top-frecency sites in the places database - * and stores them in _sites. - * The number of sites created is controlled by _placesSitesLimit. - */ - getSitesFromPlaces: function() { - let _placesSitesLimit = Services.prefs.getIntPref( - "permissions.places-sites-limit"); - if (_placesSitesLimit <= 0) { - return; - } - if (_placesSitesLimit > this.PLACES_SITES_LIMIT_MAX) { - _placesSitesLimit = this.PLACES_SITES_LIMIT_MAX; - } - - gSitesStmt.params.limit = _placesSitesLimit; - gSitesStmt.executeAsync({ - handleResult: function(aResults) { - AboutPermissions.startSitesListBatch(); - let row; - while (row = aResults.getNextRow()) { - let spec = row.getResultByName("url"); - let uri = NetUtil.newURI(spec); - let principal = gSecMan.getNoAppCodebasePrincipal(uri); - - AboutPermissions.addPrincipal(principal); - } - AboutPermissions.endSitesListBatch(); - }, - handleError: function(aError) { - Cu.reportError("AboutPermissions: " + aError); - }, - handleCompletion: function(aReason) { - // Notify oberservers for testing purposes. - AboutPermissions._initPlacesDone = true; - if (AboutPermissions._initServicesDone) { - Services.obs.notifyObservers( - null, "browser-permissions-initialized", null); - } - } - }); - }, - - /** - * Drives getEnumerateServicesGenerator to work in intervals. - */ - enumerateServicesDriver: function() { - if (this.enumerateServicesGenerator.next()) { - // Build top sitesList items faster so that the list never seems sparse - let delay = Math.min(this.sitesList.itemCount * 5, this.LIST_BUILD_DELAY); - setTimeout(this.enumerateServicesDriver.bind(this), delay); - } else { - this.enumerateServicesGenerator.close(); - this._initServicesDone = true; - if (this._initPlacesDone) { - Services.obs.notifyObservers( - null, "browser-permissions-initialized", null); - } - } - }, - - /** - * Finds sites that have non-default permissions and creates Site objects - * for them if they are not already stored in _sites. - */ - getEnumerateServicesGenerator: function() { - let itemCnt = 1; - let schemeChrome = "chrome"; - - try { - let logins = Services.logins.getAllLogins(); - logins.forEach(function(aLogin) { - try { - // aLogin.hostname is a string in origin URL format - // (e.g. "http://foo.com"). - // newURI will throw for add-ons logins stored in chrome:// URIs - // i.e.: "chrome://weave" (Sync) - if (!aLogin.hostname.startsWith(schemeChrome + ":")) { - let uri = NetUtil.newURI(aLogin.hostname); - let principal = gSecMan.getNoAppCodebasePrincipal(uri); - this.addPrincipal(principal); - } - } catch (e) { - Cu.reportError("AboutPermissions: " + e); - } - itemCnt++; - }, this); - - let disabledHosts = Services.logins.getAllDisabledHosts(); - disabledHosts.forEach(function(aHostname) { - try { - // aHostname is a string in origin URL format (e.g. "http://foo.com"). - // newURI will throw for add-ons logins stored in chrome:// URIs - // i.e.: "chrome://weave" (Sync) - if (!aHostname.startsWith(schemeChrome + ":")) { - let uri = NetUtil.newURI(aHostname); - let principal = gSecMan.getNoAppCodebasePrincipal(uri); - this.addPrincipal(principal); - } - } catch (e) { - Cu.reportError("AboutPermissions: " + e); - } - itemCnt++; - }, this); - } catch (e) { - if (!e.message.includes(MASTER_PASSWORD_MESSAGE)) { - Cu.reportError("AboutPermissions: " + e); - } - } - - let enumerator = Services.perms.enumerator; - while (enumerator.hasMoreElements()) { - let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission); - // Only include sites with exceptions set for supported permission types. - if (this._supportedPermissions.indexOf(permission.type) != -1) { - this.addPrincipal(permission.principal); - } - itemCnt++; - } - - yield false; - }, - - /** - * Creates a new Site and adds it to _sites if it's not already there. - * - * @param aPrincipal - * A principal. - */ - addPrincipal: function(aPrincipal) { - if (aPrincipal.origin in this._sites) { - return; - } - let site = new Site(aPrincipal); - this._sites[aPrincipal.origin] = site; - this.addToSitesList(site); - }, - - /** - * Populates sites-list richlistbox with data from Site object. - * - * @param aSite - * A Site object. - */ - addToSitesList: function(aSite) { - let item = document.createElement("richlistitem"); - item.setAttribute("class", "site"); - item.setAttribute("value", aSite.principal.origin); - - aSite.getFavicon(function(aURL) { - item.setAttribute("favicon", aURL); - }); - aSite.listitem = item; - - // Make sure to only display relevant items when list is filtered. - let filterValue = this.sitesFilter.value.toLowerCase(); - item.collapsed = aSite.principal.origin.toLowerCase().indexOf(filterValue) == -1; - - (this._listFragment || this.sitesList).appendChild(item); - }, - - startSitesListBatch: function() { - if (!this._listFragment) - this._listFragment = document.createDocumentFragment(); - }, - - endSitesListBatch: function() { - if (this._listFragment) { - this.sitesList.appendChild(this._listFragment); - this._listFragment = null; - } - }, - - /** - * Hides sites in richlistbox based on search text in sites-filter textbox. - */ - filterSitesList: function() { - let siteItems = this.sitesList.children; - let filterValue = this.sitesFilter.value.toLowerCase(); - - if (filterValue == "") { - for (let i = 0, iLen = siteItems.length; i < iLen; i++) { - siteItems[i].collapsed = false; - } - return; - } - - for (let i = 0, iLen = siteItems.length; i < iLen; i++) { - let siteValue = siteItems[i].value.toLowerCase(); - siteItems[i].collapsed = siteValue.indexOf(filterValue) == -1; - } - }, - - /** - * Removes all evidence of the selected site. The "forget this site" observer - * will call deleteFromSitesList to update the UI. - */ - forgetSite: function() { - this._selectedSite.forgetSite(); - }, - - /** - * Deletes sites for a host and all of its sub-domains. Removes these sites - * from _sites and removes their corresponding elements from the DOM. - * - * @param aHost - * The host string corresponding to the site to delete. - */ - deleteFromSitesList: function(aHost) { - for (let origin in this._sites) { - let site = this._sites[origin]; - if (site.principal.URI.host.hasRootDomain(aHost)) { - if (site == this._selectedSite) { - // Replace site-specific interface with "All Sites" interface. - this.sitesList.selectedItem = - document.getElementById("all-sites-item"); - } - - this.sitesList.removeChild(site.listitem); - delete this._sites[site.principal.origin]; - } - } - }, - - /** - * Shows interface for managing site-specific permissions. - */ - onSitesListSelect: function(event) { - if (event.target.selectedItem.id == "all-sites-item") { - // Clear the header label value from the previously selected site. - document.getElementById("site-label").value = ""; - this.manageDefaultPermissions(); - return; - } - - let origin = event.target.value; - let site = this._selectedSite = this._sites[origin]; - document.getElementById("site-label").value = origin; - document.getElementById("header-deck").selectedPanel = - document.getElementById("site-header"); - - this.updateVisitCount(); - this.updatePermissionsBox(); - }, - - /** - * Shows interface for managing default permissions. This corresponds to - * the "All Sites" list item. - */ - manageDefaultPermissions: function() { - this._selectedSite = null; - - document.getElementById("header-deck").selectedPanel = - document.getElementById("defaults-header"); - - this.updatePermissionsBox(); - }, - - /** - * Updates permissions interface based on selected site. - */ - updatePermissionsBox: function() { - this._supportedPermissions.forEach(function(aType) { - this.updatePermission(aType); - }, this); - - this.updatePasswordsCount(); - this.updateCookiesCount(); - }, - - /** - * Sets menulist for a given permission to the correct state, based on - * the stored permission. - * - * @param aType - * The permission type string stored in permission manager. - * e.g. "cookie", "geo", "popup", "image" - */ - updatePermission: function(aType) { - let allowItem = document.getElementById( - aType + "-" + PermissionDefaults.ALLOW); - allowItem.hidden = !this._selectedSite && - this._noGlobalAllow.indexOf(aType) != -1; - let denyItem = document.getElementById( - aType + "-" + PermissionDefaults.DENY); - denyItem.hidden = !this._selectedSite && - this._noGlobalDeny.indexOf(aType) != -1; - - let permissionMenulist = document.getElementById(aType + "-menulist"); - let permissionSetDefault = document.getElementById(aType + "-set-default"); - let permissionValue; - let permissionDefault; - let pluginPermissionEntry; - let elementsPrefSetDefault = document.querySelectorAll(".pref-set-default"); - if (!this._selectedSite) { - let _visibility = "collapse"; - for (let i = 0, iLen = elementsPrefSetDefault.length; i < iLen; i++) { - elementsPrefSetDefault[i].style.visibility = _visibility; - } - permissionSetDefault.style.visibility = _visibility; - // If there is no selected site, we are updating the default permissions - // interface. - permissionValue = PermissionDefaults[aType]; - permissionDefault = permissionValue; - if (aType == "image") { - // (aType + "-3") corresponds to ALLOW_FIRST_PARTY_ONLY, - // which is reserved for global preferences only. - document.getElementById(aType + "-3").hidden = false; - } else if (aType == "cookie") { - // (aType + "-9") corresponds to ALLOW_FIRST_PARTY_ONLY, - // which is reserved for site-specific preferences only. - document.getElementById(aType + "-9").hidden = true; - } else if (aType.startsWith("plugin")) { - pluginPermissionEntry = document.getElementById(aType + "-entry"); - pluginPermissionEntry.setAttribute("vulnerable", ""); - let vulnerable = false; - if (pluginPermissionEntry.isBlocklisted()) { - permissionMenulist.disabled = true; - permissionMenulist.setAttribute("tooltiptext", - AboutPermissions._stringBundleAboutPermissions - .GetStringFromName("pluginBlocklisted")); - vulnerable = true; - } else { - permissionMenulist.disabled = false; - permissionMenulist.setAttribute("tooltiptext", ""); - } - if (Services.prefs.getBoolPref("plugins.click_to_play") || vulnerable) { - document.getElementById(aType + "-0").disabled = false; - } else { - document.getElementById(aType + "-0").disabled = true; - } - } - } else { - let _visibility = "visible"; - for (let i = 0, iLen = elementsPrefSetDefault.length; i < iLen; i++) { - elementsPrefSetDefault[i].style.visibility = _visibility; - } - permissionSetDefault.style.visibility = _visibility; - permissionDefault = PermissionDefaults[aType]; - if (aType == "image") { - document.getElementById(aType + "-3").hidden = true; - } else if (aType == "cookie") { - document.getElementById(aType + "-9").hidden = false; - } else if (aType.startsWith("plugin")) { - pluginPermissionEntry = document.getElementById(aType + "-entry"); - let permString = pluginPermissionEntry.getAttribute("permString"); - let vulnerable = false; - if (permString.startsWith("plugin-vulnerable:")) { - let nameVulnerable = " \u2014 " - + AboutPermissions._stringBundleBrowser - .GetStringFromName("pluginActivateVulnerable.label"); - pluginPermissionEntry.setAttribute("vulnerable", nameVulnerable); - vulnerable = true; - } - if (Services.prefs.getBoolPref("plugins.click_to_play") || vulnerable) { - document.getElementById(aType + "-0").disabled = false; - } else { - document.getElementById(aType + "-0").disabled = true; - } - permissionMenulist.disabled = false; - permissionMenulist.setAttribute("tooltiptext", ""); - } - let result = {}; - permissionValue = this._selectedSite.getPermission(aType, result) ? - result.value : permissionDefault; - } - - if (aType == "image") { - if (document.getElementById(aType + "-" + permissionValue).hidden) { - // ALLOW - permissionValue = 1; - } - } - if (aType.startsWith("plugin")) { - if (document.getElementById(aType + "-" + permissionValue).disabled) { - // ALLOW - permissionValue = 1; - } - } - - if (!aType.startsWith("plugin")) { - let _elementDefault = document.getElementById(aType + "-default"); - if (!this._selectedSite || (permissionValue == permissionDefault)) { - _elementDefault.setAttribute("value", ""); - } else { - _elementDefault.setAttribute("value", "*"); - } - } else { - let _elementDefaultVisibility; - if (!this._selectedSite || (permissionValue == permissionDefault)) { - _elementDefaultVisibility = false; - } else { - _elementDefaultVisibility = true; - } - pluginPermissionEntry.setDefaultVisibility(_elementDefaultVisibility); - } - - permissionMenulist.selectedItem = document.getElementById( - aType + "-" + permissionValue); - }, - - onPermissionCommand: function(event, _default) { - let pluginHost = Cc["@mozilla.org/plugin/host;1"] - .getService(Ci.nsIPluginHost); - let permissionMimeType = event.currentTarget.getAttribute("mimeType"); - let permissionType = event.currentTarget.getAttribute("type"); - let permissionValue = event.target.value; - - if (!this._selectedSite) { - if (permissionType.startsWith("plugin")) { - let addonValue = AddonManager.STATE_ASK_TO_ACTIVATE; - switch(permissionValue) { - case "1": - addonValue = false; - break; - case "2": - addonValue = true; - break; - } - - AddonManager.getAddonsByTypes(["plugin"], function(addons) { - for (let addon of addons) { - for (let type of addon.pluginMimeTypes) { - if ((type.type == gFlash.type) && (addon.name != gFlash.name)) { - continue; - } - if (type.type.toLowerCase() == permissionMimeType.toLowerCase()) { - addon.userDisabled = addonValue; - return; - } - } - } - }); - } else { - // If there is no selected site, we are setting the default permission. - PermissionDefaults[permissionType] = permissionValue; - } - } else { - if (_default) { - this._selectedSite.clearPermission(permissionType); - } else { - this._selectedSite.setPermission(permissionType, permissionValue); - } - } - }, - - updateVisitCount: function() { - this._selectedSite.getVisitCount(function(aCount) { - let visitForm = AboutPermissions._stringBundleAboutPermissions - .GetStringFromName("visitCount"); - let visitLabel = PluralForm.get(aCount, visitForm) - .replace("#1", aCount); - document.getElementById("site-visit-count").value = visitLabel; - }); - }, - - updatePasswordsCount: function() { - if (!this._selectedSite) { - document.getElementById("passwords-count").hidden = true; - document.getElementById("passwords-manage-all-button").hidden = false; - return; - } - - let passwordsCount = this._selectedSite.logins.length; - let passwordsForm = this._stringBundleAboutPermissions - .GetStringFromName("passwordsCount"); - let passwordsLabel = PluralForm.get(passwordsCount, passwordsForm) - .replace("#1", passwordsCount); - - document.getElementById("passwords-label").value = passwordsLabel; - document.getElementById("passwords-manage-button").disabled = - (passwordsCount < 1); - document.getElementById("passwords-manage-all-button").hidden = true; - document.getElementById("passwords-count").hidden = false; - }, - - /** - * Opens password manager dialog. - */ - managePasswords: function() { - let selectedOrigin = ""; - if (this._selectedSite) { - selectedOrigin = this._selectedSite.principal.URI.prePath; - } - - let win = Services.wm.getMostRecentWindow("Toolkit:PasswordManager"); - if (win) { - win.setFilter(selectedOrigin); - win.focus(); - } else { - window.openDialog("chrome://passwordmgr/content/passwordManager.xul", - "Toolkit:PasswordManager", "", - {filterString : selectedOrigin}); - } - }, - - domainFromHost: function(aHost) { - let domain = aHost; - try { - domain = Services.eTLD.getBaseDomainFromHost(aHost); - } catch (e) { - // getBaseDomainFromHost will fail if the host is an IP address - // or is empty. - } - - return domain; - }, - - updateCookiesCount: function() { - if (!this._selectedSite) { - document.getElementById("cookies-count").hidden = true; - document.getElementById("cookies-clear-all-button").hidden = false; - document.getElementById("cookies-manage-all-button").hidden = false; - return; - } - - let cookiesCount = this._selectedSite.cookies.length; - let cookiesForm = this._stringBundleAboutPermissions - .GetStringFromName("cookiesCount"); - let cookiesLabel = PluralForm.get(cookiesCount, cookiesForm) - .replace("#1", cookiesCount); - - document.getElementById("cookies-label").value = cookiesLabel; - document.getElementById("cookies-clear-button").disabled = - (cookiesCount < 1); - document.getElementById("cookies-manage-button").disabled = - (cookiesCount < 1); - document.getElementById("cookies-clear-all-button").hidden = true; - document.getElementById("cookies-manage-all-button").hidden = true; - document.getElementById("cookies-count").hidden = false; - }, - - /** - * Clears cookies for the selected site and base domain. - */ - clearCookies: function() { - if (!this._selectedSite) { - return; - } - let site = this._selectedSite; - site.clearCookies(site.cookies); - this.updateCookiesCount(); - }, - - /** - * Opens cookie manager dialog. - */ - manageCookies: function() { - // Cookies are stored by-host, and thus we filter the cookie window - // using only the host of the selected principal's origin - let selectedHost = ""; - let selectedDomain = ""; - if (this._selectedSite) { - selectedHost = this._selectedSite.principal.URI.host; - selectedDomain = this.domainFromHost(selectedHost); - } - - let win = Services.wm.getMostRecentWindow("Browser:Cookies"); - if (win) { - win.gCookiesWindow.setFilter(selectedDomain); - win.focus(); - } else { - window.openDialog("chrome://browser/content/preferences/cookies.xul", - "Browser:Cookies", "", {filterString : selectedDomain}); - } - }, - - /** - * Focusses the filter box. - */ - focusFilterBox: function() { - this.sitesFilter.focus(); - } -} - -// See toolkit/forgetaboutsite/ForgetAboutSite.jsm -String.prototype.hasRootDomain = function(aDomain) { - let index = this.indexOf(aDomain); - if (index == -1) { - return false; - } - - if (this == aDomain) { - return true; - } - - let prevChar = this[index - 1]; - return (index == (this.length - aDomain.length)) && - (prevChar == "." || prevChar == "/"); -} diff --git a/components/permissions/aboutPermissions.xml b/components/permissions/aboutPermissions.xml deleted file mode 100644 index 2932ea0..0000000 --- a/components/permissions/aboutPermissions.xml +++ /dev/null @@ -1,113 +0,0 @@ -<?xml version="1.0"?> -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<!DOCTYPE bindings [ -<!ENTITY % aboutPermissionsDTD SYSTEM "chrome://browser/locale/permissions/aboutPermissions.dtd" > -%aboutPermissionsDTD; -]> - -<bindings xmlns="http://www.mozilla.org/xbl" - xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:xbl="http://www.mozilla.org/xbl"> - <binding id="site" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem"> - <content> - <xul:hbox class="site-container" align="center" flex="1"> - <xul:image xbl:inherits="src=favicon" class="site-favicon"/> - <xul:label xbl:inherits="value,selected" class="site-domain" crop="end" flex="1"/> - </xul:hbox> - </content> - </binding> - - <binding id="pluginPermission"> - <content> - <xul:hbox flex="1" align="baseline"> - <xul:label xbl:inherits="value=label" class="plugins-label"/> - <xul:label xbl:inherits="value=vulnerable" class="plugins-vulnerable"/> - <xul:label xbl:inherits="value=default" anonid="plugins-default" class="plugins-default"/> - <xul:spacer flex="1"/> - <xul:menulist anonid="plugins-menulist" - class="pref-menulist" - oncommand="AboutPermissions.onPermissionCommand(event, false);"> - <xul:menupopup> - <xul:menuitem anonid="ask" value="0" label="&permission.alwaysAsk;"/> - <xul:menuitem anonid="allow" value="1" label="&permission.allow;"/> - <xul:menuitem anonid="block" value="2" label="&permission.block;"/> - </xul:menupopup> - </xul:menulist> - <xul:button xbl:inherits="value=set-default" - anonid="plugins-set-default" - class="pref-set-default" - label="&permission.default;" - oncommand="AboutPermissions.onPermissionCommand(event, true);"/> - </xul:hbox> - </content> - <implementation> - <constructor><![CDATA[ - let mimeType = this.getAttribute("mimeType"); - let permString = this.getAttribute("permString"); - let menulist = document.getAnonymousElementByAttribute(this, "anonid", "plugins-menulist"); - menulist.setAttribute("id", permString + "-menulist"); - menulist.setAttribute("mimeType", mimeType); - menulist.setAttribute("type", permString); - let askitem = document.getAnonymousElementByAttribute(this, "anonid", "ask"); - askitem.setAttribute("id", permString + "-0"); - let allowitem = document.getAnonymousElementByAttribute(this, "anonid", "allow"); - allowitem.setAttribute("id", permString + "-1"); - let blockitem = document.getAnonymousElementByAttribute(this, "anonid", "block"); - blockitem.setAttribute("id", permString + "-2"); - let _default = document.getAnonymousElementByAttribute(this, "anonid", "plugins-default"); - this.setDefaultVisibility(false); - _default.setAttribute("value", "*"); - let _setDefault = document.getAnonymousElementByAttribute(this, "anonid", "plugins-set-default"); - _setDefault.setAttribute("id", permString + "-set-default"); - _setDefault.setAttribute("class", "pref-set-default"); - _setDefault.setAttribute("type", permString); - ]]> - </constructor> - <method name="setDefaultVisibility"> - <parameter name="visibility" /> - <body><![CDATA[ - let _default = document.getAnonymousElementByAttribute(this, "anonid", "plugins-default"); - if (visibility) { - _default.style.visibility = "visible"; - } else { - _default.style.visibility = "hidden"; - } - ]]> - </body> - </method> - <method name="isClickToPlay"> - <body><![CDATA[ - let pluginHost = Components.classes["@mozilla.org/plugin/host;1"] - .getService(Components.interfaces.nsIPluginHost); - let mimeType = this.getAttribute("mimeType"); - return (pluginHost.getStateForType(mimeType) - == Components.interfaces.nsIPluginTag.STATE_CLICKTOPLAY); - ]]> - </body> - </method> - <method name="isBlocklisted"> - <body><![CDATA[ - let pluginHost = Components.classes["@mozilla.org/plugin/host;1"] - .getService(Components.interfaces.nsIPluginHost); - let blocklistService = Components.classes["@mozilla.org/extensions/blocklist;1"] - .getService(Components.interfaces.nsIBlocklistService); - let mimeType = this.getAttribute("mimeType"); - let tags = pluginHost.getPluginTags(); - let blocklistState = Components.interfaces.nsIBlocklistService.STATE_NOT_BLOCKED; - for (let plugin of tags) { - if (plugin.getMimeTypes()[0] == mimeType) { - blocklistState = blocklistService.getPluginBlocklistState(plugin); - break; - } - } - return (blocklistState == Components.interfaces.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE || - blocklistState == Components.interfaces.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE); - ]]> - </body> - </method> - </implementation> - </binding> -</bindings> diff --git a/components/permissions/aboutPermissions.xul b/components/permissions/aboutPermissions.xul deleted file mode 100644 index dfee147..0000000 --- a/components/permissions/aboutPermissions.xul +++ /dev/null @@ -1,313 +0,0 @@ -<?xml version="1.0"?> -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> -<?xml-stylesheet href="chrome://browser/content/permissions/aboutPermissions.css"?> -<?xml-stylesheet href="chrome://browser/skin/permissions/aboutPermissions.css"?> - -<!DOCTYPE page [ -<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" > -%brandDTD; -<!ENTITY % aboutPermissionsDTD SYSTEM "chrome://browser/locale/permissions/aboutPermissions.dtd" > -%aboutPermissionsDTD; -]> - -<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:xhtml="http://www.w3.org/1999/xhtml" - id="permissions-page" title="&permissionsManager.title;" - onload="AboutPermissions.init();" - onunload="AboutPermissions.cleanUp();" - disablefastfind="true" - role="application"> - - <script type="application/javascript" - src="chrome://browser/content/permissions/aboutPermissions.js"/> - - <keyset> - <key key="&focusSearch.key;" modifiers="accel" oncommand="AboutPermissions.focusFilterBox();"/> - </keyset> - - <hbox id="permissions-header"> - <label id="permissions-pagetitle">&permissionsManager.title;</label> - </hbox> - <hbox flex="1" id="permissions-content" class="main-content"> - - <vbox id="sites-box"> - <button id="sites-reload" - label="&permissions.sitesReload;" - oncommand="AboutPermissions.sitesReload();"/> - <textbox id="sites-filter" - emptytext="&sites.search;" - oncommand="AboutPermissions.filterSitesList();" - type="search"/> - <richlistbox id="sites-list" - flex="1" - class="list" - onselect="AboutPermissions.onSitesListSelect(event);"> - <richlistitem id="all-sites-item" - class="site" - value="&sites.allSites;"/> - </richlistbox> - </vbox> - - <vbox id="permissions-box" flex="1"> - - <deck id="header-deck"> - <hbox id="site-header" class="pref-item" align="center"> - <description id="site-description"> - &header.site.start;<label id="site-label"/>&header.site.end; - </description> - <label id="site-visit-count"/> - <spacer flex="1"/> - <button id="forget-site-button" - label="&permissions.forgetSite;" - oncommand="AboutPermissions.forgetSite();"/> - </hbox> - - <hbox id="defaults-header" class="pref-item" align="center"> - <description id="defaults-description"> - &header.defaults; - </description> - </hbox> - </deck> - - <!-- Passwords --> - <hbox id="password-pref-item" - class="pref-item" align="top"> - <image class="pref-icon" type="password"/> - <vbox> - <hbox> - <label class="pref-title" value="&password.label;"/> - <label id="password-default" class="pref-default" value="*"/> - </hbox> - <hbox align="center"> - <menulist id="password-menulist" - class="pref-menulist" - type="password" - oncommand="AboutPermissions.onPermissionCommand(event, false);"> - <menupopup> - <menuitem id="password-1" value="1" label="&permission.allow;"/> - <menuitem id="password-2" value="2" label="&permission.block;"/> - </menupopup> - </menulist> - <button id="password-set-default" - class="pref-set-default" - label="&permission.default;" - type="password" - oncommand="AboutPermissions.onPermissionCommand(event, true);"/> - <button id="passwords-manage-all-button" - label="&password.manage;" - oncommand="AboutPermissions.managePasswords();"/> - </hbox> - <hbox id="passwords-count" align="center"> - <label id="passwords-label"/> - <button id="passwords-manage-button" - label="&password.manage;" - oncommand="AboutPermissions.managePasswords();"/> - </hbox> - </vbox> - </hbox> - - <!-- Image Blocking --> - <hbox id="image-pref-item" - class="pref-item" align="top"> - <image class="pref-icon" type="image"/> - <vbox> - <hbox> - <label class="pref-title" value="&image.label;"/> - <label id="image-default" class="pref-default" value="*"/> - </hbox> - <hbox> - <menulist id="image-menulist" - class="pref-menulist" - type="image" - oncommand="AboutPermissions.onPermissionCommand(event, false);"> - <menupopup> - <menuitem id="image-1" value="1" label="&permission.allow;"/> - <menuitem id="image-2" value="2" label="&permission.block;"/> - <menuitem id="image-3" value="3" label="&permission.allowFirstPartyOnly;"/> - </menupopup> - </menulist> - <button id="image-set-default" - class="pref-set-default" - label="&permission.default;" - type="image" - oncommand="AboutPermissions.onPermissionCommand(event, true);"/> - </hbox> - </vbox> - </hbox> - - <!-- Pop-up Blocking --> - <hbox id="popup-pref-item" - class="pref-item" align="top"> - <image class="pref-icon" type="popup"/> - <vbox> - <hbox> - <label class="pref-title" value="&popup.label;"/> - <label id="popup-default" class="pref-default" value="*"/> - </hbox> - <hbox> - <menulist id="popup-menulist" - class="pref-menulist" - type="popup" - oncommand="AboutPermissions.onPermissionCommand(event, false);"> - <menupopup> - <menuitem id="popup-1" value="1" label="&permission.allow;"/> - <menuitem id="popup-2" value="2" label="&permission.block;"/> - </menupopup> - </menulist> - <button id="popup-set-default" - class="pref-set-default" - label="&permission.default;" - type="popup" - oncommand="AboutPermissions.onPermissionCommand(event, true);"/> - </hbox> - </vbox> - </hbox> - - <!-- Cookies --> - <hbox id="cookie-pref-item" - class="pref-item" align="top"> - <image class="pref-icon" type="cookie"/> - <vbox> - <hbox> - <label class="pref-title" value="&cookie.label;"/> - <label id="cookie-default" class="pref-default" value="*"/> - </hbox> - <hbox align="center"> - <menulist id="cookie-menulist" - class="pref-menulist" - type="cookie" - oncommand="AboutPermissions.onPermissionCommand(event, false);"> - <menupopup> - <menuitem id="cookie-1" value="1" label="&permission.allow;"/> - <menuitem id="cookie-8" value="8" label="&permission.allowForSession;"/> - <menuitem id="cookie-9" value="9" label="&permission.allowFirstPartyOnly;"/> - <menuitem id="cookie-2" value="2" label="&permission.block;"/> - </menupopup> - </menulist> - <button id="cookie-set-default" - class="pref-set-default" - label="&permission.default;" - type="cookie" - oncommand="AboutPermissions.onPermissionCommand(event, true);"/> - <button id="cookies-clear-all-button" - label="&cookie.removeAll;" - oncommand="Services.cookies.removeAll();"/> - <button id="cookies-manage-all-button" - label="&cookie.manage;" - oncommand="AboutPermissions.manageCookies();"/> - </hbox> - <hbox id="cookies-count" align="center"> - <label id="cookies-label"/> - <button id="cookies-clear-button" - label="&cookie.remove;" - oncommand="AboutPermissions.clearCookies();"/> - <button id="cookies-manage-button" - label="&cookie.manage;" - oncommand="AboutPermissions.manageCookies();"/> - </hbox> - </vbox> - </hbox> - - <!-- Desktop Notifications --> - <hbox id="desktop-notification-pref-item" - class="pref-item" align="top"> - <image class="pref-icon" type="desktop-notification"/> - <vbox> - <hbox> - <label class="pref-title" value="&desktop-notification.label;"/> - <label id="desktop-notification-default" class="pref-default" value="*"/> - </hbox> - <hbox> - <menulist id="desktop-notification-menulist" - class="pref-menulist" - type="desktop-notification" - oncommand="AboutPermissions.onPermissionCommand(event, false);"> - <menupopup> - <menuitem id="desktop-notification-0" value="0" label="&permission.alwaysAsk;"/> - <menuitem id="desktop-notification-1" value="1" label="&permission.allow;"/> - <menuitem id="desktop-notification-2" value="2" label="&permission.block;"/> - </menupopup> - </menulist> - <button id="desktop-notification-set-default" - class="pref-set-default" - label="&permission.default;" - type="desktop-notification" - oncommand="AboutPermissions.onPermissionCommand(event, true);"/> - </hbox> - </vbox> - </hbox> - - <!-- Addons Blocking --> - <hbox id="install-pref-item" - class="pref-item" align="top"> - <image class="pref-icon" type="install"/> - <vbox> - <hbox> - <label class="pref-title" value="&install.label;"/> - <label id="install-default" class="pref-default" value="*"/> - </hbox> - <hbox> - <menulist id="install-menulist" - class="pref-menulist" - type="install" - oncommand="AboutPermissions.onPermissionCommand(event, false);"> - <menupopup> - <menuitem id="install-1" value="1" label="&permission.allow;"/> - <menuitem id="install-2" value="2" label="&permission.block;"/> - </menupopup> - </menulist> - <button id="install-set-default" - class="pref-set-default" - label="&permission.default;" - type="install" - oncommand="AboutPermissions.onPermissionCommand(event, true);"/> - </hbox> - </vbox> - </hbox> - - <!-- Geolocation --> - <hbox id="geo-pref-item" - class="pref-item" align="top"> - <image class="pref-icon" type="geo"/> - <vbox> - <hbox> - <label class="pref-title" value="&geo.label;"/> - <label id="geo-default" class="pref-default" value="*"/> - </hbox> - <hbox> - <menulist id="geo-menulist" - class="pref-menulist" - type="geo" - oncommand="AboutPermissions.onPermissionCommand(event, false);"> - <menupopup> - <menuitem id="geo-0" value="0" label="&permission.alwaysAsk;"/> - <menuitem id="geo-1" value="1" label="&permission.allow;"/> - <menuitem id="geo-2" value="2" label="&permission.block;"/> - </menupopup> - </menulist> - <button id="geo-set-default" - class="pref-set-default" - label="&permission.default;" - type="geo" - oncommand="AboutPermissions.onPermissionCommand(event, true);"/> - </hbox> - </vbox> - </hbox> - - <!-- Opt-in activation of Plug-ins --> - <hbox id="plugins-pref-item" - class="pref-item" align="top"> - <image class="pref-icon" type="plugins"/> - <vbox> - <label class="pref-title" value="&plugins.label;"/> - <vbox id="plugins-box"/> - </vbox> - </hbox> - </vbox> - </hbox> - -</page> diff --git a/components/permissions/jar.mn b/components/permissions/jar.mn deleted file mode 100644 index c788938..0000000 --- a/components/permissions/jar.mn +++ /dev/null @@ -1,9 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -browser.jar: - content/browser/permissions/aboutPermissions.xul - content/browser/permissions/aboutPermissions.js - content/browser/permissions/aboutPermissions.css - content/browser/permissions/aboutPermissions.xml diff --git a/components/permissions/moz.build b/components/permissions/moz.build deleted file mode 100644 index 3bbe672..0000000 --- a/components/permissions/moz.build +++ /dev/null @@ -1,7 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -JAR_MANIFESTS += ['jar.mn'] diff --git a/components/places/PlacesUIUtils.jsm b/components/places/PlacesUIUtils.jsm deleted file mode 100644 index e3a9e13..0000000 --- a/components/places/PlacesUIUtils.jsm +++ /dev/null @@ -1,1373 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -this.EXPORTED_SYMBOLS = ["PlacesUIUtils"]; - -var Ci = Components.interfaces; -var Cc = Components.classes; -var Cr = Components.results; -var Cu = Components.utils; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "PluralForm", - "resource://gre/modules/PluralForm.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", - "resource://gre/modules/PrivateBrowsingUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow", - "resource:///modules/RecentWindow.jsm"); - -XPCOMUtils.defineLazyGetter(this, "PlacesUtils", function() { - Cu.import("resource://gre/modules/PlacesUtils.jsm"); - return PlacesUtils; -}); - -this.PlacesUIUtils = { - ORGANIZER_LEFTPANE_VERSION: 7, - ORGANIZER_FOLDER_ANNO: "PlacesOrganizer/OrganizerFolder", - ORGANIZER_QUERY_ANNO: "PlacesOrganizer/OrganizerQuery", - - LOAD_IN_SIDEBAR_ANNO: "bookmarkProperties/loadInSidebar", - DESCRIPTION_ANNO: "bookmarkProperties/description", - - TYPE_TAB_DROP: "application/x-moz-tabbrowser-tab", - - /** - * Makes a URI from a spec, and do fixup - * @param aSpec - * The string spec of the URI - * @returns A URI object for the spec. - */ - createFixedURI: function PUIU_createFixedURI(aSpec) { - return URIFixup.createFixupURI(aSpec, Ci.nsIURIFixup.FIXUP_FLAG_NONE); - }, - - getFormattedString: function PUIU_getFormattedString(key, params) { - return bundle.formatStringFromName(key, params, params.length); - }, - - /** - * Get a localized plural string for the specified key name and numeric value - * substituting parameters. - * - * @param aKey - * String, key for looking up the localized string in the bundle - * @param aNumber - * Number based on which the final localized form is looked up - * @param aParams - * Array whose items will substitute #1, #2,... #n parameters - * in the string. - * - * @see https://developer.mozilla.org/en/Localization_and_Plurals - * @return The localized plural string. - */ - getPluralString: function PUIU_getPluralString(aKey, aNumber, aParams) { - let str = PluralForm.get(aNumber, bundle.GetStringFromName(aKey)); - - // Replace #1 with aParams[0], #2 with aParams[1], and so on. - return str.replace(/\#(\d+)/g, function (matchedId, matchedNumber) { - let param = aParams[parseInt(matchedNumber, 10) - 1]; - return param !== undefined ? param : matchedId; - }); - }, - - getString: function PUIU_getString(key) { - return bundle.GetStringFromName(key); - }, - - get _copyableAnnotations() [ - this.DESCRIPTION_ANNO, - this.LOAD_IN_SIDEBAR_ANNO, - PlacesUtils.POST_DATA_ANNO, - PlacesUtils.READ_ONLY_ANNO, - ], - - /** - * Get a transaction for copying a uri item (either a bookmark or a history - * entry) from one container to another. - * - * @param aData - * JSON object of dropped or pasted item properties - * @param aContainer - * The container being copied into - * @param aIndex - * The index within the container the item is copied to - * @return A nsITransaction object that performs the copy. - * - * @note Since a copy creates a completely new item, only some internal - * annotations are synced from the old one. - * @see this._copyableAnnotations for the list of copyable annotations. - */ - _getURIItemCopyTransaction: - function PUIU__getURIItemCopyTransaction(aData, aContainer, aIndex) - { - let transactions = []; - if (aData.dateAdded) { - transactions.push( - new PlacesEditItemDateAddedTransaction(null, aData.dateAdded) - ); - } - if (aData.lastModified) { - transactions.push( - new PlacesEditItemLastModifiedTransaction(null, aData.lastModified) - ); - } - - let keyword = aData.keyword || null; - let annos = []; - if (aData.annos) { - annos = aData.annos.filter(function (aAnno) { - return this._copyableAnnotations.indexOf(aAnno.name) != -1; - }, this); - } - - return new PlacesCreateBookmarkTransaction(PlacesUtils._uri(aData.uri), - aContainer, aIndex, aData.title, - keyword, annos, transactions); - }, - - /** - * Gets a transaction for copying (recursively nesting to include children) - * a folder (or container) and its contents from one folder to another. - * - * @param aData - * Unwrapped dropped folder data - Obj containing folder and children - * @param aContainer - * The container we are copying into - * @param aIndex - * The index in the destination container to insert the new items - * @return A nsITransaction object that will perform the copy. - * - * @note Since a copy creates a completely new item, only some internal - * annotations are synced from the old one. - * @see this._copyableAnnotations for the list of copyable annotations. - */ - _getFolderCopyTransaction(aData, aContainer, aIndex) { - function getChildItemsTransactions(aRoot) { - let transactions = []; - let index = aIndex; - for (let i = 0; i < aRoot.childCount; ++i) { - let child = aRoot.getChild(i); - // Temporary hacks until we switch to PlacesTransactions.jsm. - let isLivemark = - PlacesUtils.annotations.itemHasAnnotation(child.itemId, - PlacesUtils.LMANNO_FEEDURI); - let [node] = PlacesUtils.unwrapNodes( - PlacesUtils.wrapNode(child, PlacesUtils.TYPE_X_MOZ_PLACE, isLivemark), - PlacesUtils.TYPE_X_MOZ_PLACE - ); - - // Make sure that items are given the correct index, this will be - // passed by the transaction manager to the backend for the insertion. - // Insertion behaves differently for DEFAULT_INDEX (append). - if (aIndex != PlacesUtils.bookmarks.DEFAULT_INDEX) { - index = i; - } - - if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER) { - if (node.livemark && node.annos) { - transactions.push( - PlacesUIUtils._getLivemarkCopyTransaction(node, aContainer, index) - ); - } - else { - transactions.push( - PlacesUIUtils._getFolderCopyTransaction(node, aContainer, index) - ); - } - } - else if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR) { - transactions.push(new PlacesCreateSeparatorTransaction(-1, index)); - } - else if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE) { - transactions.push( - PlacesUIUtils._getURIItemCopyTransaction(node, -1, index) - ); - } - else { - throw new Error("Unexpected item under a bookmarks folder"); - } - } - return transactions; - } - - if (aContainer == PlacesUtils.tagsFolderId) { // Copying into a tag folder. - let transactions = []; - if (!aData.livemark && aData.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER) { - let {root} = PlacesUtils.getFolderContents(aData.id, false, false); - let urls = PlacesUtils.getURLsForContainerNode(root); - root.containerOpen = false; - for (let { uri } of urls) { - transactions.push( - new PlacesTagURITransaction(NetUtil.newURI(uri), [aData.title]) - ); - } - } - return new PlacesAggregatedTransaction("addTags", transactions); - } - - if (aData.livemark && aData.annos) { // Copying a livemark. - return this._getLivemarkCopyTransaction(aData, aContainer, aIndex); - } - - let {root} = PlacesUtils.getFolderContents(aData.id, false, false); - let transactions = getChildItemsTransactions(root); - root.containerOpen = false; - - if (aData.dateAdded) { - transactions.push( - new PlacesEditItemDateAddedTransaction(null, aData.dateAdded) - ); - } - if (aData.lastModified) { - transactions.push( - new PlacesEditItemLastModifiedTransaction(null, aData.lastModified) - ); - } - - let annos = []; - if (aData.annos) { - annos = aData.annos.filter(function (aAnno) { - return this._copyableAnnotations.indexOf(aAnno.name) != -1; - }, this); - } - - return new PlacesCreateFolderTransaction(aData.title, aContainer, aIndex, - annos, transactions); - }, - - /** - * Gets a transaction for copying a live bookmark item from one container to - * another. - * - * @param aData - * Unwrapped live bookmarkmark data - * @param aContainer - * The container we are copying into - * @param aIndex - * The index in the destination container to insert the new items - * @return A nsITransaction object that will perform the copy. - * - * @note Since a copy creates a completely new item, only some internal - * annotations are synced from the old one. - * @see this._copyableAnnotations for the list of copyable annotations. - */ - _getLivemarkCopyTransaction: - function PUIU__getLivemarkCopyTransaction(aData, aContainer, aIndex) - { - if (!aData.livemark || !aData.annos) { - throw new Error("node is not a livemark"); - } - - let feedURI, siteURI; - let annos = []; - if (aData.annos) { - annos = aData.annos.filter(function (aAnno) { - if (aAnno.name == PlacesUtils.LMANNO_FEEDURI) { - feedURI = PlacesUtils._uri(aAnno.value); - } - else if (aAnno.name == PlacesUtils.LMANNO_SITEURI) { - siteURI = PlacesUtils._uri(aAnno.value); - } - return this._copyableAnnotations.indexOf(aAnno.name) != -1 - }, this); - } - - return new PlacesCreateLivemarkTransaction(feedURI, siteURI, aData.title, - aContainer, aIndex, annos); - }, - - /** - * Test if a bookmark item = a live bookmark item. - * - * @param aItemId - * item identifier - * @return true if a live bookmark item, false otherwise. - * - * @note Maybe this should be removed later, see bug 1072833. - */ - _isLivemark: - function PUIU__isLivemark(aItemId) - { - // Since this check may be done on each dragover event, it's worth maintaining - // a cache. - let self = PUIU__isLivemark; - if (!("ids" in self)) { - const LIVEMARK_ANNO = PlacesUtils.LMANNO_FEEDURI; - - let idsVec = PlacesUtils.annotations.getItemsWithAnnotation(LIVEMARK_ANNO); - self.ids = new Set(idsVec); - - let obs = Object.freeze({ - QueryInterface: XPCOMUtils.generateQI(Ci.nsIAnnotationObserver), - - onItemAnnotationSet(itemId, annoName) { - if (annoName == LIVEMARK_ANNO) - self.ids.add(itemId); - }, - - onItemAnnotationRemoved(itemId, annoName) { - // If annoName is set to an empty string, the item is gone. - if (annoName == LIVEMARK_ANNO || annoName == "") - self.ids.delete(itemId); - }, - - onPageAnnotationSet() { }, - onPageAnnotationRemoved() { }, - }); - PlacesUtils.annotations.addObserver(obs); - PlacesUtils.registerShutdownFunction(() => { - PlacesUtils.annotations.removeObserver(obs); - }); - } - return self.ids.has(aItemId); - }, - - /** - * Constructs a Transaction for the drop or paste of a blob of data into - * a container. - * @param data - * The unwrapped data blob of dropped or pasted data. - * @param type - * The content type of the data - * @param container - * The container the data was dropped or pasted into - * @param index - * The index within the container the item was dropped or pasted at - * @param copy - * The drag action was copy, so don't move folders or links. - * @returns An object implementing nsITransaction that can perform - * the move/insert. - */ - makeTransaction: - function PUIU_makeTransaction(data, type, container, index, copy) - { - switch (data.type) { - case PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER: - if (copy) { - return this._getFolderCopyTransaction(data, container, index); - } - - // Otherwise move the item. - return new PlacesMoveItemTransaction(data.id, container, index); - break; - case PlacesUtils.TYPE_X_MOZ_PLACE: - if (copy || data.id == -1) { // Id is -1 if the place is not bookmarked. - return this._getURIItemCopyTransaction(data, container, index); - } - - // Otherwise move the item. - return new PlacesMoveItemTransaction(data.id, container, index); - break; - case PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR: - if (copy) { - // There is no data in a separator, so copying it just amounts to - // inserting a new separator. - return new PlacesCreateSeparatorTransaction(container, index); - } - - // Otherwise move the item. - return new PlacesMoveItemTransaction(data.id, container, index); - break; - default: - if (type == PlacesUtils.TYPE_X_MOZ_URL || - type == PlacesUtils.TYPE_UNICODE || - type == this.TYPE_TAB_DROP) { - let title = type != PlacesUtils.TYPE_UNICODE ? data.title - : data.uri; - return new PlacesCreateBookmarkTransaction(PlacesUtils._uri(data.uri), - container, index, title); - } - } - return null; - }, - - /** - * Shows the bookmark dialog corresponding to the specified info. - * - * @param aInfo - * Describes the item to be edited/added in the dialog. - * See documentation at the top of bookmarkProperties.js - * @param aWindow - * Owner window for the new dialog. - * - * @see documentation at the top of bookmarkProperties.js - * @return true if any transaction has been performed, false otherwise. - */ - showBookmarkDialog: - function PUIU_showBookmarkDialog(aInfo, aParentWindow) { - // Preserve size attributes differently based on the fact the dialog has - // a folder picker or not, since it needs more horizontal space than the - // other controls. - let hasFolderPicker = !("hiddenRows" in aInfo) || - aInfo.hiddenRows.indexOf("folderPicker") == -1; - // Use a different chrome url to persist different sizes. - let dialogURL = hasFolderPicker ? - "chrome://browser/content/places/bookmarkProperties2.xul" : - "chrome://browser/content/places/bookmarkProperties.xul"; - - let features = "centerscreen,chrome,modal,resizable=yes"; - aParentWindow.openDialog(dialogURL, "", features, aInfo); - return ("performed" in aInfo && aInfo.performed); - }, - - _getTopBrowserWin: function PUIU__getTopBrowserWin() { - return RecentWindow.getMostRecentBrowserWindow(); - }, - - /** - * Returns the closet ancestor places view for the given DOM node - * @param aNode - * a DOM node - * @return the closet ancestor places view if exists, null otherwsie. - */ - getViewForNode: function PUIU_getViewForNode(aNode) { - let node = aNode; - - // The view for a <menu> of which its associated menupopup is a places - // view, is the menupopup. - if (node.localName == "menu" && !node._placesNode && - node.lastChild._placesView) - return node.lastChild._placesView; - - while (node instanceof Ci.nsIDOMElement) { - if (node._placesView) - return node._placesView; - if (node.localName == "tree" && node.getAttribute("type") == "places") - return node; - - node = node.parentNode; - } - - return null; - }, - - /** - * By calling this before visiting an URL, the visit will be associated to a - * TRANSITION_TYPED transition (if there is no a referrer). - * This is used when visiting pages from the history menu, history sidebar, - * url bar, url autocomplete results, and history searches from the places - * organizer. If this is not called visits will be marked as - * TRANSITION_LINK. - */ - markPageAsTyped: function PUIU_markPageAsTyped(aURL) { - PlacesUtils.history.markPageAsTyped(this.createFixedURI(aURL)); - }, - - /** - * By calling this before visiting an URL, the visit will be associated to a - * TRANSITION_BOOKMARK transition. - * This is used when visiting pages from the bookmarks menu, - * personal toolbar, and bookmarks from within the places organizer. - * If this is not called visits will be marked as TRANSITION_LINK. - */ - markPageAsFollowedBookmark: function PUIU_markPageAsFollowedBookmark(aURL) { - PlacesUtils.history.markPageAsFollowedBookmark(this.createFixedURI(aURL)); - }, - - /** - * By calling this before visiting an URL, any visit in frames will be - * associated to a TRANSITION_FRAMED_LINK transition. - * This is actually used to distinguish user-initiated visits in frames - * so automatic visits can be correctly ignored. - */ - markPageAsFollowedLink: function PUIU_markPageAsFollowedLink(aURL) { - PlacesUtils.history.markPageAsFollowedLink(this.createFixedURI(aURL)); - }, - - /** - * Allows opening of javascript/data URI only if the given node is - * bookmarked (see bug 224521). - * @param aURINode - * a URI node - * @param aWindow - * a window on which a potential error alert is shown on. - * @return true if it's safe to open the node in the browser, false otherwise. - * - */ - checkURLSecurity: function PUIU_checkURLSecurity(aURINode, aWindow) { - if (PlacesUtils.nodeIsBookmark(aURINode)) - return true; - - var uri = PlacesUtils._uri(aURINode.uri); - if (uri.schemeIs("javascript") || uri.schemeIs("data")) { - const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties"; - var brandShortName = Cc["@mozilla.org/intl/stringbundle;1"]. - getService(Ci.nsIStringBundleService). - createBundle(BRANDING_BUNDLE_URI). - GetStringFromName("brandShortName"); - - var errorStr = this.getString("load-js-data-url-error"); - Services.prompt.alert(aWindow, brandShortName, errorStr); - return false; - } - return true; - }, - - /** - * Get the description associated with a document, as specified in a <META> - * element. - * @param doc - * A DOM Document to get a description for - * @returns A description string if a META element was discovered with a - * "description" or "httpequiv" attribute, empty string otherwise. - */ - getDescriptionFromDocument: function PUIU_getDescriptionFromDocument(doc) { - var metaElements = doc.getElementsByTagName("META"); - for (var i = 0; i < metaElements.length; ++i) { - if (metaElements[i].name.toLowerCase() == "description" || - metaElements[i].httpEquiv.toLowerCase() == "description") { - return metaElements[i].content; - } - } - return ""; - }, - - /** - * Retrieve the description of an item - * @param aItemId - * item identifier - * @returns the description of the given item, or an empty string if it is - * not set. - */ - getItemDescription: function PUIU_getItemDescription(aItemId) { - if (PlacesUtils.annotations.itemHasAnnotation(aItemId, this.DESCRIPTION_ANNO)) - return PlacesUtils.annotations.getItemAnnotation(aItemId, this.DESCRIPTION_ANNO); - return ""; - }, - - /** - * Check whether or not the given node represents a removable entry (either in - * history or in bookmarks). - * - * @param aNode - * a node, except the root node of a query. - * @return true if the aNode represents a removable entry, false otherwise. - */ - canUserRemove: function (aNode) { - let parentNode = aNode.parent; - if (!parentNode) - throw new Error("canUserRemove doesn't accept root nodes"); - - // If it's not a bookmark, we can remove it unless it's a child of a - // livemark. - if (aNode.itemId == -1) { - // Rather than executing a db query, checking the existence of the feedURI - // annotation, detect livemark children by the fact that they are the only - // direct non-bookmark children of bookmark folders. - return !PlacesUtils.nodeIsFolder(parentNode); - } - - // Generally it's always possible to remove children of a query. - if (PlacesUtils.nodeIsQuery(parentNode)) - return true; - - // Otherwise it has to be a child of an editable folder. - return !this.isContentsReadOnly(parentNode); - }, - - /** - * DO NOT USE THIS API IN ADDONS. IT IS VERY LIKELY TO CHANGE WHEN THE SWITCH - * TO GUIDS IS COMPLETE (BUG 1071511). - * - * Check whether or not the given node or item-id points to a folder which - * should not be modified by the user (i.e. its children should be unremovable - * and unmovable, new children should be disallowed, etc). - * These semantics are not inherited, meaning that read-only folder may - * contain editable items (for instance, the places root is read-only, but all - * of its direct children aren't). - * - * You should only pass folder item ids or folder nodes for aNodeOrItemId. - * While this is only enforced for the node case (if an item id of a separator - * or a bookmark is passed, false is returned), it's considered the caller's - * job to ensure that it checks a folder. - * Also note that folder-shortcuts should only be passed as result nodes. - * Otherwise they are just treated as bookmarks (i.e. false is returned). - * - * @param aNodeOrItemId - * any item id or result node. - * @throws if aNodeOrItemId is neither an item id nor a folder result node. - * @note livemark "folders" are considered read-only (but see bug 1072833). - * @return true if aItemId points to a read-only folder, false otherwise. - */ - isContentsReadOnly: function (aNodeOrItemId) { - let itemId; - if (typeof(aNodeOrItemId) == "number") { - itemId = aNodeOrItemId; - } - else if (PlacesUtils.nodeIsFolder(aNodeOrItemId)) { - itemId = PlacesUtils.getConcreteItemId(aNodeOrItemId); - } - else { - throw new Error("invalid value for aNodeOrItemId"); - } - - if (itemId == PlacesUtils.placesRootId || this._isLivemark(itemId)) - return true; - - // leftPaneFolderId, and as a result, allBookmarksFolderId, is a lazy getter - // performing at least a synchronous DB query (and on its very first call - // in a fresh profile, it also creates the entire structure). - // Therefore we don't want to this function, which is called very often by - // isCommandEnabled, to ever be the one that invokes it first, especially - // because isCommandEnabled may be called way before the left pane folder is - // even created (for example, if the user only uses the bookmarks menu or - // toolbar for managing bookmarks). To do so, we avoid comparing to those - // special folder if the lazy getter is still in place. This is safe merely - // because the only way to access the left pane contents goes through - // "resolving" the leftPaneFolderId getter. - if ("get" in Object.getOwnPropertyDescriptor(this, "leftPaneFolderId")) - return false; - - return itemId == this.leftPaneFolderId || - itemId == this.allBookmarksFolderId; - }, - - /** - * Gives the user a chance to cancel loading lots of tabs at once - */ - _confirmOpenInTabs: - function PUIU__confirmOpenInTabs(numTabsToOpen, aWindow) { - const WARN_ON_OPEN_PREF = "browser.tabs.warnOnOpen"; - var reallyOpen = true; - - if (Services.prefs.getBoolPref(WARN_ON_OPEN_PREF)) { - if (numTabsToOpen >= Services.prefs.getIntPref("browser.tabs.maxOpenBeforeWarn")) { - // default to true: if it were false, we wouldn't get this far - var warnOnOpen = { value: true }; - - var messageKey = "tabs.openWarningMultipleBranded"; - var openKey = "tabs.openButtonMultiple"; - const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties"; - var brandShortName = Cc["@mozilla.org/intl/stringbundle;1"]. - getService(Ci.nsIStringBundleService). - createBundle(BRANDING_BUNDLE_URI). - GetStringFromName("brandShortName"); - - var buttonPressed = Services.prompt.confirmEx( - aWindow, - this.getString("tabs.openWarningTitle"), - this.getFormattedString(messageKey, [numTabsToOpen, brandShortName]), - (Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) + - (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1), - this.getString(openKey), null, null, - this.getFormattedString("tabs.openWarningPromptMeBranded", - [brandShortName]), - warnOnOpen - ); - - reallyOpen = (buttonPressed == 0); - // don't set the pref unless they press OK and it's false - if (reallyOpen && !warnOnOpen.value) - Services.prefs.setBoolPref(WARN_ON_OPEN_PREF, false); - } - } - - return reallyOpen; - }, - - /** aItemsToOpen needs to be an array of objects of the form: - * {uri: string, isBookmark: boolean} - */ - _openTabset: function PUIU__openTabset(aItemsToOpen, aEvent, aWindow) { - if (!aItemsToOpen.length) - return; - - // Prefer the caller window if it's a browser window, otherwise use - // the top browser window. - var browserWindow = null; - browserWindow = - aWindow && aWindow.document.documentElement.getAttribute("windowtype") == "navigator:browser" ? - aWindow : this._getTopBrowserWin(); - - var urls = []; - let skipMarking = browserWindow && PrivateBrowsingUtils.isWindowPrivate(browserWindow); - for (let item of aItemsToOpen) { - urls.push(item.uri); - if (skipMarking) { - continue; - } - - if (item.isBookmark) - this.markPageAsFollowedBookmark(item.uri); - else - this.markPageAsTyped(item.uri); - } - - // whereToOpenLink doesn't return "window" when there's no browser window - // open (Bug 630255). - var where = browserWindow ? - browserWindow.whereToOpenLink(aEvent, false, true) : "window"; - if (where == "window") { - // There is no browser window open, thus open a new one. - var uriList = PlacesUtils.toISupportsString(urls.join("|")); - var args = Cc["@mozilla.org/supports-array;1"]. - createInstance(Ci.nsISupportsArray); - args.AppendElement(uriList); - browserWindow = Services.ww.openWindow(aWindow, - "chrome://browser/content/browser.xul", - null, "chrome,dialog=no,all", args); - return; - } - - var loadInBackground = where == "tabshifted" ? true : false; - // For consistency, we want all the bookmarks to open in new tabs, instead - // of having one of them replace the currently focused tab. Hence we call - // loadTabs with aReplace set to false. - browserWindow.gBrowser.loadTabs(urls, loadInBackground, false); - }, - - openLiveMarkNodesInTabs: - function PUIU_openLiveMarkNodesInTabs(aNode, aEvent, aView) { - let window = aView.ownerWindow; - - PlacesUtils.livemarks.getLivemark({id: aNode.itemId}) - .then(aLivemark => { - urlsToOpen = []; - - let nodes = aLivemark.getNodesForContainer(aNode); - for (let node of nodes) { - urlsToOpen.push({uri: node.uri, isBookmark: false}); - } - - if (this._confirmOpenInTabs(urlsToOpen.length, window)) { - this._openTabset(urlsToOpen, aEvent, window); - } - }, Cu.reportError); - }, - - openContainerNodeInTabs: - function PUIU_openContainerInTabs(aNode, aEvent, aView) { - let window = aView.ownerWindow; - - let urlsToOpen = PlacesUtils.getURLsForContainerNode(aNode); - if (this._confirmOpenInTabs(urlsToOpen.length, window)) { - this._openTabset(urlsToOpen, aEvent, window); - } - }, - - openURINodesInTabs: function PUIU_openURINodesInTabs(aNodes, aEvent, aView) { - let window = aView.ownerWindow; - - let urlsToOpen = []; - for (var i=0; i < aNodes.length; i++) { - // Skip over separators and folders. - if (PlacesUtils.nodeIsURI(aNodes[i])) - urlsToOpen.push({uri: aNodes[i].uri, isBookmark: PlacesUtils.nodeIsBookmark(aNodes[i])}); - } - this._openTabset(urlsToOpen, aEvent, window); - }, - - /** - * Loads the node's URL in the appropriate tab or window or as a web - * panel given the user's preference specified by modifier keys tracked by a - * DOM mouse/key event. - * @param aNode - * An uri result node. - * @param aEvent - * The DOM mouse/key event with modifier keys set that track the - * user's preferred destination window or tab. - * @param aView - * The controller associated with aNode. - */ - openNodeWithEvent: - function PUIU_openNodeWithEvent(aNode, aEvent, aView) { - let window = aView.ownerWindow; - this._openNodeIn(aNode, window.whereToOpenLink(aEvent, false, true), window); - }, - - /** - * Loads the node's URL in the appropriate tab or window or as a - * web panel. - * see also openUILinkIn - */ - openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aView, aPrivate) { - let window = aView.ownerWindow; - this._openNodeIn(aNode, aWhere, window, aPrivate); - }, - - _openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aWindow, aPrivate=false) { - if (aNode && PlacesUtils.nodeIsURI(aNode) && - this.checkURLSecurity(aNode, aWindow)) { - let isBookmark = PlacesUtils.nodeIsBookmark(aNode); - - if (!PrivateBrowsingUtils.isWindowPrivate(aWindow)) { - if (isBookmark) - this.markPageAsFollowedBookmark(aNode.uri); - else - this.markPageAsTyped(aNode.uri); - } - - // Check whether the node is a bookmark which should be opened as - // a web panel - if (aWhere == "current" && isBookmark) { - if (PlacesUtils.annotations - .itemHasAnnotation(aNode.itemId, this.LOAD_IN_SIDEBAR_ANNO)) { - let browserWin = this._getTopBrowserWin(); - if (browserWin) { - browserWin.openWebPanel(aNode.title, aNode.uri); - return; - } - } - } - aWindow.openUILinkIn(aNode.uri, aWhere, { - inBackground: Services.prefs.getBoolPref("browser.tabs.loadBookmarksInBackground"), - private: aPrivate, - }); - } - }, - - /** - * Helper for guessing scheme from an url string. - * Used to avoid nsIURI overhead in frequently called UI functions. - * - * @param aUrlString the url to guess the scheme from. - * - * @return guessed scheme for this url string. - * - * @note this is not supposed be perfect, so use it only for UI purposes. - */ - guessUrlSchemeForUI: function PUIU_guessUrlSchemeForUI(aUrlString) { - return aUrlString.substr(0, aUrlString.indexOf(":")); - }, - - getBestTitle: function PUIU_getBestTitle(aNode, aDoNotCutTitle) { - var title; - if (!aNode.title && PlacesUtils.nodeIsURI(aNode)) { - // if node title is empty, try to set the label using host and filename - // PlacesUtils._uri() will throw if aNode.uri is not a valid URI - try { - var uri = PlacesUtils._uri(aNode.uri); - var host = uri.host; - var fileName = uri.QueryInterface(Ci.nsIURL).fileName; - // if fileName is empty, use path to distinguish labels - if (aDoNotCutTitle) { - title = host + uri.path; - } else { - title = host + (fileName ? - (host ? "/" + this.ellipsis + "/" : "") + fileName : - uri.path); - } - } - catch (e) { - // Use (no title) for non-standard URIs (data:, javascript:, ...) - title = ""; - } - } - else - title = aNode.title; - - return title || this.getString("noTitle"); - }, - - get leftPaneQueries() { - // build the map - this.leftPaneFolderId; - return this.leftPaneQueries; - }, - - // Get the folder id for the organizer left-pane folder. - get leftPaneFolderId() { - let leftPaneRoot = -1; - let allBookmarksId; - - // Shortcuts to services. - let bs = PlacesUtils.bookmarks; - let as = PlacesUtils.annotations; - - // This is the list of the left pane queries. - let queries = { - "PlacesRoot": { title: "" }, - "History": { title: this.getString("OrganizerQueryHistory") }, - "Downloads": { title: this.getString("OrganizerQueryDownloads") }, - "Tags": { title: this.getString("OrganizerQueryTags") }, - "AllBookmarks": { title: this.getString("OrganizerQueryAllBookmarks") }, - "BookmarksToolbar": - { title: null, - concreteTitle: PlacesUtils.getString("BookmarksToolbarFolderTitle"), - concreteId: PlacesUtils.toolbarFolderId }, - "BookmarksMenu": - { title: null, - concreteTitle: PlacesUtils.getString("BookmarksMenuFolderTitle"), - concreteId: PlacesUtils.bookmarksMenuFolderId }, - "UnfiledBookmarks": - { title: null, - concreteTitle: PlacesUtils.getString("UnsortedBookmarksFolderTitle"), - concreteId: PlacesUtils.unfiledBookmarksFolderId }, - }; - // All queries but PlacesRoot. - const EXPECTED_QUERY_COUNT = 7; - - // Removes an item and associated annotations, ignoring eventual errors. - function safeRemoveItem(aItemId) { - try { - if (as.itemHasAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO) && - !(as.getItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO) in queries)) { - // Some extension annotated their roots with our query annotation, - // so we should not delete them. - return; - } - // removeItemAnnotation does not check if item exists, nor the anno, - // so this is safe to do. - as.removeItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_FOLDER_ANNO); - as.removeItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO); - // This will throw if the annotation is an orphan. - bs.removeItem(aItemId); - } - catch(e) { /* orphan anno */ } - } - - // Returns true if item really exists, false otherwise. - function itemExists(aItemId) { - try { - bs.getItemIndex(aItemId); - return true; - } - catch(e) { - return false; - } - } - - // Get all items marked as being the left pane folder. - let items = as.getItemsWithAnnotation(this.ORGANIZER_FOLDER_ANNO); - if (items.length > 1) { - // Something went wrong, we cannot have more than one left pane folder, - // remove all left pane folders and continue. We will create a new one. - items.forEach(safeRemoveItem); - } - else if (items.length == 1 && items[0] != -1) { - leftPaneRoot = items[0]; - // Check that organizer left pane root is valid. - let version = as.getItemAnnotation(leftPaneRoot, this.ORGANIZER_FOLDER_ANNO); - if (version != this.ORGANIZER_LEFTPANE_VERSION || - !itemExists(leftPaneRoot)) { - // Invalid root, we must rebuild the left pane. - safeRemoveItem(leftPaneRoot); - leftPaneRoot = -1; - } - } - - if (leftPaneRoot != -1) { - // A valid left pane folder has been found. - // Build the leftPaneQueries Map. This is used to quickly access them, - // associating a mnemonic name to the real item ids. - delete this.leftPaneQueries; - this.leftPaneQueries = {}; - - let items = as.getItemsWithAnnotation(this.ORGANIZER_QUERY_ANNO); - // While looping through queries we will also check for their validity. - let queriesCount = 0; - for (let i = 0; i < items.length; i++) { - let queryName = as.getItemAnnotation(items[i], this.ORGANIZER_QUERY_ANNO); - - // Some extension did use our annotation to decorate their items - // with icons, so we should check only our elements, to avoid dataloss. - if (!(queryName in queries)) - continue; - - let query = queries[queryName]; - query.itemId = items[i]; - - if (!itemExists(query.itemId)) { - // Orphan annotation, bail out and create a new left pane root. - break; - } - - // Check that all queries have valid parents. - let parentId = bs.getFolderIdForItem(query.itemId); - if (items.indexOf(parentId) == -1 && parentId != leftPaneRoot) { - // The parent is not part of the left pane, bail out and create a new - // left pane root. - break; - } - - // Titles could have been corrupted or the user could have changed his - // locale. Check title and eventually fix it. - if (bs.getItemTitle(query.itemId) != query.title) - bs.setItemTitle(query.itemId, query.title); - if ("concreteId" in query) { - if (bs.getItemTitle(query.concreteId) != query.concreteTitle) - bs.setItemTitle(query.concreteId, query.concreteTitle); - } - - // Add the query to our cache. - this.leftPaneQueries[queryName] = query.itemId; - queriesCount++; - } - - if (queriesCount != EXPECTED_QUERY_COUNT) { - // Queries number is wrong, so the left pane must be corrupt. - // Note: we can't just remove the leftPaneRoot, because some query could - // have a bad parent, so we have to remove all items one by one. - items.forEach(safeRemoveItem); - safeRemoveItem(leftPaneRoot); - } - else { - // Everything is fine, return the current left pane folder. - delete this.leftPaneFolderId; - return this.leftPaneFolderId = leftPaneRoot; - } - } - - // Create a new left pane folder. - var callback = { - // Helper to create an organizer special query. - create_query: function CB_create_query(aQueryName, aParentId, aQueryUrl) { - let itemId = bs.insertBookmark(aParentId, - PlacesUtils._uri(aQueryUrl), - bs.DEFAULT_INDEX, - queries[aQueryName].title); - // Mark as special organizer query. - as.setItemAnnotation(itemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO, aQueryName, - 0, as.EXPIRE_NEVER); - // We should never backup this, since it changes between profiles. - as.setItemAnnotation(itemId, PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO, 1, - 0, as.EXPIRE_NEVER); - // Add to the queries map. - PlacesUIUtils.leftPaneQueries[aQueryName] = itemId; - return itemId; - }, - - // Helper to create an organizer special folder. - create_folder: function CB_create_folder(aFolderName, aParentId, aIsRoot) { - // Left Pane Root Folder. - let folderId = bs.createFolder(aParentId, - queries[aFolderName].title, - bs.DEFAULT_INDEX); - // We should never backup this, since it changes between profiles. - as.setItemAnnotation(folderId, PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO, 1, - 0, as.EXPIRE_NEVER); - - if (aIsRoot) { - // Mark as special left pane root. - as.setItemAnnotation(folderId, PlacesUIUtils.ORGANIZER_FOLDER_ANNO, - PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION, - 0, as.EXPIRE_NEVER); - } - else { - // Mark as special organizer folder. - as.setItemAnnotation(folderId, PlacesUIUtils.ORGANIZER_QUERY_ANNO, aFolderName, - 0, as.EXPIRE_NEVER); - PlacesUIUtils.leftPaneQueries[aFolderName] = folderId; - } - return folderId; - }, - - runBatched: function CB_runBatched(aUserData) { - delete PlacesUIUtils.leftPaneQueries; - PlacesUIUtils.leftPaneQueries = { }; - - // Left Pane Root Folder. - leftPaneRoot = this.create_folder("PlacesRoot", bs.placesRoot, true); - - // History Query. - this.create_query("History", leftPaneRoot, - "place:type=" + - Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY + - "&sort=" + - Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING); - - // Downloads. - this.create_query("Downloads", leftPaneRoot, - "place:transition=" + - Ci.nsINavHistoryService.TRANSITION_DOWNLOAD + - "&sort=" + - Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING); - - // Tags Query. - this.create_query("Tags", leftPaneRoot, - "place:type=" + - Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY + - "&sort=" + - Ci.nsINavHistoryQueryOptions.SORT_BY_TITLE_ASCENDING); - - // All Bookmarks Folder. - allBookmarksId = this.create_folder("AllBookmarks", leftPaneRoot, false); - - // All Bookmarks->Bookmarks Toolbar Query. - this.create_query("BookmarksToolbar", allBookmarksId, - "place:folder=TOOLBAR"); - - // All Bookmarks->Bookmarks Menu Query. - this.create_query("BookmarksMenu", allBookmarksId, - "place:folder=BOOKMARKS_MENU"); - - // All Bookmarks->Unfiled Bookmarks Query. - this.create_query("UnfiledBookmarks", allBookmarksId, - "place:folder=UNFILED_BOOKMARKS"); - } - }; - bs.runInBatchMode(callback, null); - // Maybe: PlacesUtils.bookmarks.runInBatchMode(callback, null); ? - - delete this.leftPaneFolderId; - return this.leftPaneFolderId = leftPaneRoot; - }, - - /** - * Get the folder id for the organizer left-pane folder. - */ - get allBookmarksFolderId() { - // ensure the left-pane root is initialized; - this.leftPaneFolderId; - delete this.allBookmarksFolderId; - return this.allBookmarksFolderId = this.leftPaneQueries["AllBookmarks"]; - }, - - /** - * If an item is a left-pane query, returns the name of the query - * or an empty string if not. - * - * @param aItemId id of a container - * @returns the name of the query, or empty string if not a left-pane query - */ - getLeftPaneQueryNameFromId: function PUIU_getLeftPaneQueryNameFromId(aItemId) { - var queryName = ""; - // If the let pane hasn't been built, use the annotation service - // directly, to avoid building the left pane too early. - if (Object.getOwnPropertyDescriptor(this, "leftPaneFolderId").value === undefined) { - try { - queryName = PlacesUtils.annotations. - getItemAnnotation(aItemId, this.ORGANIZER_QUERY_ANNO); - } - catch (ex) { - // doesn't have the annotation - queryName = ""; - } - } - else { - // If the left pane has already been built, use the name->id map - // cached in PlacesUIUtils. - for (let [name, id] in Iterator(this.leftPaneQueries)) { - if (aItemId == id) - queryName = name; - } - } - return queryName; - }, - - /** - * Returns the passed URL with a #moz-resolution fragment - * for the specified dimensions and devicePixelRatio. - * - * @param aWindow - * A window from where we want to get the device - * pixel Ratio - * - * @param aURL - * The URL where we should add the fragment - * - * @param aWidth - * The target image width - * - * @param aHeight - * The target image height - * - * @return The URL with the fragment at the end - */ - getImageURLForResolution: - function PUIU_getImageURLForResolution(aWindow, aURL, aWidth, aHeight) { - return aURL; - } -}; - -XPCOMUtils.defineLazyServiceGetter(PlacesUIUtils, "RDF", - "@mozilla.org/rdf/rdf-service;1", - "nsIRDFService"); - -XPCOMUtils.defineLazyGetter(PlacesUIUtils, "localStore", function() { - return PlacesUIUtils.RDF.GetDataSource("rdf:local-store"); -}); - -XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ellipsis", function() { - return Services.prefs.getComplexValue("intl.ellipsis", - Ci.nsIPrefLocalizedString).data; -}); - -XPCOMUtils.defineLazyServiceGetter(this, "URIFixup", - "@mozilla.org/docshell/urifixup;1", - "nsIURIFixup"); - -XPCOMUtils.defineLazyGetter(this, "bundle", function() { - const PLACES_STRING_BUNDLE_URI = - "chrome://browser/locale/places/places.properties"; - return Cc["@mozilla.org/intl/stringbundle;1"]. - getService(Ci.nsIStringBundleService). - createBundle(PLACES_STRING_BUNDLE_URI); -}); - -XPCOMUtils.defineLazyServiceGetter(this, "focusManager", - "@mozilla.org/focus-manager;1", - "nsIFocusManager"); - -/** - * This is a compatibility shim for old PUIU.ptm users. - * - * If you're looking for transactions and writing new code using them, directly - * use the transactions objects exported by the PlacesUtils.jsm module. - * - * This object will be removed once enough users are converted to the new API. - */ -XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ptm", function() { - // Ensure PlacesUtils is imported in scope. - PlacesUtils; - - return { - aggregateTransactions: function(aName, aTransactions) - new PlacesAggregatedTransaction(aName, aTransactions), - - createFolder: function(aName, aContainer, aIndex, aAnnotations, - aChildItemsTransactions) - new PlacesCreateFolderTransaction(aName, aContainer, aIndex, aAnnotations, - aChildItemsTransactions), - - createItem: function(aURI, aContainer, aIndex, aTitle, aKeyword, - aAnnotations, aChildTransactions) - new PlacesCreateBookmarkTransaction(aURI, aContainer, aIndex, aTitle, - aKeyword, aAnnotations, - aChildTransactions), - - createSeparator: function(aContainer, aIndex) - new PlacesCreateSeparatorTransaction(aContainer, aIndex), - - createLivemark: function(aFeedURI, aSiteURI, aName, aContainer, aIndex, - aAnnotations) - new PlacesCreateLivemarkTransaction(aFeedURI, aSiteURI, aName, aContainer, - aIndex, aAnnotations), - - moveItem: function(aItemId, aNewContainer, aNewIndex) - new PlacesMoveItemTransaction(aItemId, aNewContainer, aNewIndex), - - removeItem: function(aItemId) - new PlacesRemoveItemTransaction(aItemId), - - editItemTitle: function(aItemId, aNewTitle) - new PlacesEditItemTitleTransaction(aItemId, aNewTitle), - - editBookmarkURI: function(aItemId, aNewURI) - new PlacesEditBookmarkURITransaction(aItemId, aNewURI), - - setItemAnnotation: function(aItemId, aAnnotationObject) - new PlacesSetItemAnnotationTransaction(aItemId, aAnnotationObject), - - setPageAnnotation: function(aURI, aAnnotationObject) - new PlacesSetPageAnnotationTransaction(aURI, aAnnotationObject), - - editBookmarkKeyword: function(aItemId, aNewKeyword) - new PlacesEditBookmarkKeywordTransaction(aItemId, aNewKeyword), - - editBookmarkPostData: function(aItemId, aPostData) - new PlacesEditBookmarkPostDataTransaction(aItemId, aPostData), - - editLivemarkSiteURI: function(aLivemarkId, aSiteURI) - new PlacesEditLivemarkSiteURITransaction(aLivemarkId, aSiteURI), - - editLivemarkFeedURI: function(aLivemarkId, aFeedURI) - new PlacesEditLivemarkFeedURITransaction(aLivemarkId, aFeedURI), - - editItemDateAdded: function(aItemId, aNewDateAdded) - new PlacesEditItemDateAddedTransaction(aItemId, aNewDateAdded), - - editItemLastModified: function(aItemId, aNewLastModified) - new PlacesEditItemLastModifiedTransaction(aItemId, aNewLastModified), - - sortFolderByName: function(aFolderId) - new PlacesSortFolderByNameTransaction(aFolderId), - - tagURI: function(aURI, aTags) - new PlacesTagURITransaction(aURI, aTags), - - untagURI: function(aURI, aTags) - new PlacesUntagURITransaction(aURI, aTags), - - /** - * Transaction for setting/unsetting Load-in-sidebar annotation. - * - * @param aBookmarkId - * id of the bookmark where to set Load-in-sidebar annotation. - * @param aLoadInSidebar - * boolean value. - * @returns nsITransaction object. - */ - setLoadInSidebar: function(aItemId, aLoadInSidebar) - { - let annoObj = { name: PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO, - type: Ci.nsIAnnotationService.TYPE_INT32, - flags: 0, - value: aLoadInSidebar, - expires: Ci.nsIAnnotationService.EXPIRE_NEVER }; - return new PlacesSetItemAnnotationTransaction(aItemId, annoObj); - }, - - /** - * Transaction for editing the description of a bookmark or a folder. - * - * @param aItemId - * id of the item to edit. - * @param aDescription - * new description. - * @returns nsITransaction object. - */ - editItemDescription: function(aItemId, aDescription) - { - let annoObj = { name: PlacesUIUtils.DESCRIPTION_ANNO, - type: Ci.nsIAnnotationService.TYPE_STRING, - flags: 0, - value: aDescription, - expires: Ci.nsIAnnotationService.EXPIRE_NEVER }; - return new PlacesSetItemAnnotationTransaction(aItemId, annoObj); - }, - - //////////////////////////////////////////////////////////////////////////// - //// nsITransactionManager forwarders. - - beginBatch: function() - PlacesUtils.transactionManager.beginBatch(null), - - endBatch: function() - PlacesUtils.transactionManager.endBatch(false), - - doTransaction: function(txn) - PlacesUtils.transactionManager.doTransaction(txn), - - undoTransaction: function() - PlacesUtils.transactionManager.undoTransaction(), - - redoTransaction: function() - PlacesUtils.transactionManager.redoTransaction(), - - get numberOfUndoItems() - PlacesUtils.transactionManager.numberOfUndoItems, - get numberOfRedoItems() - PlacesUtils.transactionManager.numberOfRedoItems, - get maxTransactionCount() - PlacesUtils.transactionManager.maxTransactionCount, - set maxTransactionCount(val) - PlacesUtils.transactionManager.maxTransactionCount = val, - - clear: function() - PlacesUtils.transactionManager.clear(), - - peekUndoStack: function() - PlacesUtils.transactionManager.peekUndoStack(), - - peekRedoStack: function() - PlacesUtils.transactionManager.peekRedoStack(), - - getUndoStack: function() - PlacesUtils.transactionManager.getUndoStack(), - - getRedoStack: function() - PlacesUtils.transactionManager.getRedoStack(), - - AddListener: function(aListener) - PlacesUtils.transactionManager.AddListener(aListener), - - RemoveListener: function(aListener) - PlacesUtils.transactionManager.RemoveListener(aListener) - } -}); diff --git a/components/places/content/bookmarkProperties.js b/components/places/content/bookmarkProperties.js deleted file mode 100644 index e1d1077..0000000 --- a/components/places/content/bookmarkProperties.js +++ /dev/null @@ -1,675 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * The panel is initialized based on data given in the js object passed - * as window.arguments[0]. The object must have the following fields set: - * @ action (String). Possible values: - * - "add" - for adding a new item. - * @ type (String). Possible values: - * - "bookmark" - * @ loadBookmarkInSidebar - optional, the default state for the - * "Load this bookmark in the sidebar" field. - * - "folder" - * @ URIList (Array of nsIURI objects) - optional, list of uris to - * be bookmarked under the new folder. - * - "livemark" - * @ uri (nsIURI object) - optional, the default uri for the new item. - * The property is not used for the "folder with items" type. - * @ title (String) - optional, the default title for the new item. - * @ description (String) - optional, the default description for the new - * item. - * @ defaultInsertionPoint (InsertionPoint JS object) - optional, the - * default insertion point for the new item. - * @ keyword (String) - optional, the default keyword for the new item. - * @ postData (String) - optional, POST data to accompany the keyword. - * @ charSet (String) - optional, character-set to accompany the keyword. - * Notes: - * 1) If |uri| is set for a bookmark/livemark item and |title| isn't, - * the dialog will query the history tables for the title associated - * with the given uri. If the dialog is set to adding a folder with - * bookmark items under it (see URIList), a default static title is - * used ("[Folder Name]"). - * 2) The index field of the default insertion point is ignored if - * the folder picker is shown. - * - "edit" - for editing a bookmark item or a folder. - * @ type (String). Possible values: - * - "bookmark" - * @ itemId (Integer) - the id of the bookmark item. - * - "folder" (also applies to livemarks) - * @ itemId (Integer) - the id of the folder. - * @ hiddenRows (Strings array) - optional, list of rows to be hidden - * regardless of the item edited or added by the dialog. - * Possible values: - * - "title" - * - "location" - * - "description" - * - "keyword" - * - "tags" - * - "loadInSidebar" - * - "feedLocation" - * - "siteLocation" - * - "folderPicker" - hides both the tree and the menu. - * @ readOnly (Boolean) - optional, states if the panel should be read-only - * - * window.arguments[0].performed is set to true if any transaction has - * been performed by the dialog. - */ - -Components.utils.import('resource://gre/modules/XPCOMUtils.jsm'); -XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", - "resource://gre/modules/PrivateBrowsingUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Task", - "resource://gre/modules/Task.jsm"); - -const BOOKMARK_ITEM = 0; -const BOOKMARK_FOLDER = 1; -const LIVEMARK_CONTAINER = 2; - -const ACTION_EDIT = 0; -const ACTION_ADD = 1; - -var elementsHeight = new Map(); - -var BookmarkPropertiesPanel = { - - /** UI Text Strings */ - __strings: null, - get _strings() { - if (!this.__strings) { - this.__strings = document.getElementById("stringBundle"); - } - return this.__strings; - }, - - _action: null, - _itemType: null, - _itemId: -1, - _uri: null, - _loadInSidebar: false, - _title: "", - _description: "", - _URIs: [], - _keyword: "", - _postData: null, - _charSet: "", - _feedURI: null, - _siteURI: null, - - _defaultInsertionPoint: null, - _hiddenRows: [], - _batching: false, - _readOnly: false, - - /** - * This method returns the correct label for the dialog's "accept" - * button based on the variant of the dialog. - */ - _getAcceptLabel: function BPP__getAcceptLabel() { - if (this._action == ACTION_ADD) { - if (this._URIs.length) - return this._strings.getString("dialogAcceptLabelAddMulti"); - - if (this._itemType == LIVEMARK_CONTAINER) - return this._strings.getString("dialogAcceptLabelAddLivemark"); - - if (this._dummyItem || this._loadInSidebar) - return this._strings.getString("dialogAcceptLabelAddItem"); - - return this._strings.getString("dialogAcceptLabelSaveItem"); - } - return this._strings.getString("dialogAcceptLabelEdit"); - }, - - /** - * This method returns the correct title for the current variant - * of this dialog. - */ - _getDialogTitle: function BPP__getDialogTitle() { - if (this._action == ACTION_ADD) { - if (this._itemType == BOOKMARK_ITEM) - return this._strings.getString("dialogTitleAddBookmark"); - if (this._itemType == LIVEMARK_CONTAINER) - return this._strings.getString("dialogTitleAddLivemark"); - - // add folder - NS_ASSERT(this._itemType == BOOKMARK_FOLDER, "Unknown item type"); - if (this._URIs.length) - return this._strings.getString("dialogTitleAddMulti"); - - return this._strings.getString("dialogTitleAddFolder"); - } - if (this._action == ACTION_EDIT) { - return this._strings.getFormattedString("dialogTitleEdit", [this._title]); - } - return ""; - }, - - /** - * Determines the initial data for the item edited or added by this dialog - */ - _determineItemInfo: function BPP__determineItemInfo() { - var dialogInfo = window.arguments[0]; - this._action = dialogInfo.action == "add" ? ACTION_ADD : ACTION_EDIT; - this._hiddenRows = dialogInfo.hiddenRows ? dialogInfo.hiddenRows : []; - if (this._action == ACTION_ADD) { - NS_ASSERT("type" in dialogInfo, "missing type property for add action"); - - if ("title" in dialogInfo) - this._title = dialogInfo.title; - - if ("defaultInsertionPoint" in dialogInfo) { - this._defaultInsertionPoint = dialogInfo.defaultInsertionPoint; - } - else - this._defaultInsertionPoint = - new InsertionPoint(PlacesUtils.bookmarksMenuFolderId, - PlacesUtils.bookmarks.DEFAULT_INDEX, - Ci.nsITreeView.DROP_ON); - - switch (dialogInfo.type) { - case "bookmark": - this._itemType = BOOKMARK_ITEM; - if ("uri" in dialogInfo) { - NS_ASSERT(dialogInfo.uri instanceof Ci.nsIURI, - "uri property should be a uri object"); - this._uri = dialogInfo.uri; - if (typeof(this._title) != "string") { - this._title = this._getURITitleFromHistory(this._uri) || - this._uri.spec; - } - } - else { - this._uri = PlacesUtils._uri("about:blank"); - this._title = this._strings.getString("newBookmarkDefault"); - this._dummyItem = true; - } - - if ("loadBookmarkInSidebar" in dialogInfo) - this._loadInSidebar = dialogInfo.loadBookmarkInSidebar; - - if ("keyword" in dialogInfo) { - this._keyword = dialogInfo.keyword; - this._isAddKeywordDialog = true; - if ("postData" in dialogInfo) - this._postData = dialogInfo.postData; - if ("charSet" in dialogInfo) - this._charSet = dialogInfo.charSet; - } - break; - - case "folder": - this._itemType = BOOKMARK_FOLDER; - if (!this._title) { - if ("URIList" in dialogInfo) { - this._title = this._strings.getString("bookmarkAllTabsDefault"); - this._URIs = dialogInfo.URIList; - } - else - this._title = this._strings.getString("newFolderDefault"); - this._dummyItem = true; - } - break; - - case "livemark": - this._itemType = LIVEMARK_CONTAINER; - if ("feedURI" in dialogInfo) - this._feedURI = dialogInfo.feedURI; - if ("siteURI" in dialogInfo) - this._siteURI = dialogInfo.siteURI; - - if (!this._title) { - if (this._feedURI) { - this._title = this._getURITitleFromHistory(this._feedURI) || - this._feedURI.spec; - } - else - this._title = this._strings.getString("newLivemarkDefault"); - } - } - - if ("description" in dialogInfo) - this._description = dialogInfo.description; - } - else { // edit - NS_ASSERT("itemId" in dialogInfo); - this._itemId = dialogInfo.itemId; - this._title = PlacesUtils.bookmarks.getItemTitle(this._itemId); - this._readOnly = !!dialogInfo.readOnly; - - switch (dialogInfo.type) { - case "bookmark": - this._itemType = BOOKMARK_ITEM; - - this._uri = PlacesUtils.bookmarks.getBookmarkURI(this._itemId); - // keyword - this._keyword = PlacesUtils.bookmarks - .getKeywordForBookmark(this._itemId); - // Load In Sidebar - this._loadInSidebar = PlacesUtils.annotations - .itemHasAnnotation(this._itemId, - PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO); - break; - - case "folder": - this._itemType = BOOKMARK_FOLDER; - PlacesUtils.livemarks.getLivemark({ id: this._itemId }) - .then(aLivemark => { - this._itemType = LIVEMARK_CONTAINER; - this._feedURI = aLivemark.feedURI; - this._siteURI = aLivemark.siteURI; - this._fillEditProperties(); - - let acceptButton = document.documentElement.getButton("accept"); - acceptButton.disabled = !this._inputIsValid(); - - let newHeight = window.outerHeight + - this._element("descriptionField").boxObject.height; - window.resizeTo(window.outerWidth, newHeight); - }, () => undefined); - - break; - } - - // Description - if (PlacesUtils.annotations - .itemHasAnnotation(this._itemId, PlacesUIUtils.DESCRIPTION_ANNO)) { - this._description = PlacesUtils.annotations - .getItemAnnotation(this._itemId, - PlacesUIUtils.DESCRIPTION_ANNO); - } - } - }, - - /** - * This method returns the title string corresponding to a given URI. - * If none is available from the bookmark service (probably because - * the given URI doesn't appear in bookmarks or history), we synthesize - * a title from the first 100 characters of the URI. - * - * @param aURI - * nsIURI object for which we want the title - * - * @returns a title string - */ - _getURITitleFromHistory: function BPP__getURITitleFromHistory(aURI) { - NS_ASSERT(aURI instanceof Ci.nsIURI); - - // get the title from History - return PlacesUtils.history.getPageTitle(aURI); - }, - - /** - * This method should be called by the onload of the Bookmark Properties - * dialog to initialize the state of the panel. - */ - onDialogLoad: Task.async(function* BPP_onDialogLoad() { - this._determineItemInfo(); - - document.title = this._getDialogTitle(); - var acceptButton = document.documentElement.getButton("accept"); - acceptButton.label = this._getAcceptLabel(); - - // Do not use sizeToContent, otherwise, due to bug 90276, the dialog will - // grow at every opening. - // Since elements can be uncollapsed asynchronously, we must observe their - // mutations and resize the dialog using a cached element size. - this._height = window.outerHeight; - this._mutationObserver = new MutationObserver(mutations => { - for (let mutation of mutations) { - let target = mutation.target; - let id = target.id; - if (!/^editBMPanel_.*(Row|Checkbox)$/.test(id)) - continue; - - let collapsed = target.getAttribute("collapsed") === "true"; - let wasCollapsed = mutation.oldValue === "true"; - if (collapsed == wasCollapsed) - continue; - - if (collapsed) { - this._height -= elementsHeight.get(id); - elementsHeight.delete(id); - } else { - elementsHeight.set(id, target.boxObject.height); - this._height += elementsHeight.get(id); - } - window.resizeTo(window.outerWidth, this._height); - } - }); - - this._mutationObserver.observe(document, - { subtree: true, - attributeOldValue: true, - attributeFilter: ["collapsed"] }); - - // Some controls are flexible and we want to update their cached size when - // the dialog is resized. - window.addEventListener("resize", this); - - this._beginBatch(); - - switch (this._action) { - case ACTION_EDIT: - this._fillEditProperties(); - acceptButton.disabled = this._readOnly; - break; - case ACTION_ADD: - yield this._fillAddProperties(); - // if this is an uri related dialog disable accept button until - // the user fills an uri value. - if (this._itemType == BOOKMARK_ITEM) - acceptButton.disabled = !this._inputIsValid(); - break; - } - - if (!this._readOnly) { - // Listen on uri fields to enable accept button if input is valid - if (this._itemType == BOOKMARK_ITEM) { - this._element("locationField") - .addEventListener("input", this, false); - if (this._isAddKeywordDialog) { - this._element("keywordField") - .addEventListener("input", this, false); - } - } - else if (this._itemType == LIVEMARK_CONTAINER) { - this._element("feedLocationField") - .addEventListener("input", this, false); - this._element("siteLocationField") - .addEventListener("input", this, false); - } - } - - // Ensure the Name Picker textbox is focused on load - var namePickerElem = document.getElementById('editBMPanel_namePicker'); - namePickerElem.focus(); - namePickerElem.select(); - }), - - // nsIDOMEventListener - handleEvent: function BPP_handleEvent(aEvent) { - var target = aEvent.target; - switch (aEvent.type) { - case "input": - if (target.id == "editBMPanel_locationField" || - target.id == "editBMPanel_feedLocationField" || - target.id == "editBMPanel_siteLocationField" || - target.id == "editBMPanel_keywordField") { - // Check uri fields to enable accept button if input is valid - document.documentElement - .getButton("accept").disabled = !this._inputIsValid(); - } - break; - case "resize": - for (let [id, oldHeight] of elementsHeight) { - let newHeight = document.getElementById(id).boxObject.height; - this._height += - oldHeight + newHeight; - elementsHeight.set(id, newHeight); - } - break; - } - }, - - _beginBatch: function BPP__beginBatch() { - if (this._batching) - return; - - PlacesUtils.transactionManager.beginBatch(null); - this._batching = true; - }, - - _endBatch: function BPP__endBatch() { - if (!this._batching) - return; - - PlacesUtils.transactionManager.endBatch(false); - this._batching = false; - }, - - _fillEditProperties: function BPP__fillEditProperties() { - gEditItemOverlay.initPanel(this._itemId, - { hiddenRows: this._hiddenRows, - forceReadOnly: this._readOnly }); - }, - - _fillAddProperties: Task.async(function* BPP__fillAddProperties() { - yield this._createNewItem(); - // Edit the new item - gEditItemOverlay.initPanel(this._itemId, - { hiddenRows: this._hiddenRows }); - // Empty location field if the uri is about:blank, this way inserting a new - // url will be easier for the user, Accept button will be automatically - // disabled by the input listener until the user fills the field. - var locationField = this._element("locationField"); - if (locationField.value == "about:blank") - locationField.value = ""; - }), - - // nsISupports - QueryInterface: function BPP_QueryInterface(aIID) { - if (aIID.equals(Ci.nsIDOMEventListener) || - aIID.equals(Ci.nsISupports)) - return this; - - throw Cr.NS_NOINTERFACE; - }, - - _element: function BPP__element(aID) { - return document.getElementById("editBMPanel_" + aID); - }, - - onDialogUnload: function BPP_onDialogUnload() { - // gEditItemOverlay does not exist anymore here, so don't rely on it. - this._mutationObserver.disconnect(); - delete this._mutationObserver; - - window.removeEventListener("resize", this); - - // Calling removeEventListener with arguments which do not identify any - // currently registered EventListener on the EventTarget has no effect. - this._element("locationField") - .removeEventListener("input", this, false); - this._element("feedLocationField") - .removeEventListener("input", this, false); - this._element("siteLocationField") - .removeEventListener("input", this, false); - }, - - onDialogAccept: function BPP_onDialogAccept() { - // We must blur current focused element to save its changes correctly - document.commandDispatcher.focusedElement.blur(); - // The order here is important! We have to uninit the panel first, otherwise - // late changes could force it to commit more transactions. - gEditItemOverlay.uninitPanel(true); - this._endBatch(); - window.arguments[0].performed = true; - }, - - onDialogCancel: function BPP_onDialogCancel() { - // The order here is important! We have to uninit the panel first, otherwise - // changes done as part of Undo may change the panel contents and by - // that force it to commit more transactions. - gEditItemOverlay.uninitPanel(true); - this._endBatch(); - PlacesUtils.transactionManager.undoTransaction(); - window.arguments[0].performed = false; - }, - - /** - * This method checks to see if the input fields are in a valid state. - * - * @returns true if the input is valid, false otherwise - */ - _inputIsValid: function BPP__inputIsValid() { - if (this._itemType == BOOKMARK_ITEM && - !this._containsValidURI("locationField")) - return false; - if (this._isAddKeywordDialog && !this._element("keywordField").value.length) - return false; - - return true; - }, - - /** - * Determines whether the XUL textbox with the given ID contains a - * string that can be converted into an nsIURI. - * - * @param aTextboxID - * the ID of the textbox element whose contents we'll test - * - * @returns true if the textbox contains a valid URI string, false otherwise - */ - _containsValidURI: function BPP__containsValidURI(aTextboxID) { - try { - var value = this._element(aTextboxID).value; - if (value) { - PlacesUIUtils.createFixedURI(value); - return true; - } - } catch (e) { } - return false; - }, - - /** - * [New Item Mode] Get the insertion point details for the new item, given - * dialog state and opening arguments. - * - * The container-identifier and insertion-index are returned separately in - * the form of [containerIdentifier, insertionIndex] - */ - _getInsertionPointDetails: function BPP__getInsertionPointDetails() { - var containerId = this._defaultInsertionPoint.itemId; - var indexInContainer = this._defaultInsertionPoint.index; - - return [containerId, indexInContainer]; - }, - - /** - * Returns a transaction for creating a new bookmark item representing the - * various fields and opening arguments of the dialog. - */ - _getCreateNewBookmarkTransaction: - function BPP__getCreateNewBookmarkTransaction(aContainer, aIndex) { - var annotations = []; - var childTransactions = []; - - if (this._description) { - let annoObj = { name : PlacesUIUtils.DESCRIPTION_ANNO, - type : Ci.nsIAnnotationService.TYPE_STRING, - flags : 0, - value : this._description, - expires: Ci.nsIAnnotationService.EXPIRE_NEVER }; - let editItemTxn = new PlacesSetItemAnnotationTransaction(-1, annoObj); - childTransactions.push(editItemTxn); - } - - if (this._loadInSidebar) { - let annoObj = { name : PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO, - value : true }; - let setLoadTxn = new PlacesSetItemAnnotationTransaction(-1, annoObj); - childTransactions.push(setLoadTxn); - } - - if (this._postData) { - let postDataTxn = new PlacesEditBookmarkPostDataTransaction(-1, this._postData); - childTransactions.push(postDataTxn); - } - - //XXX TODO: this should be in a transaction! - if (this._charSet && !PrivateBrowsingUtils.isWindowPrivate(window)) - PlacesUtils.setCharsetForURI(this._uri, this._charSet); - - let createTxn = new PlacesCreateBookmarkTransaction(this._uri, - aContainer, - aIndex, - this._title, - this._keyword, - annotations, - childTransactions); - - return new PlacesAggregatedTransaction(this._getDialogTitle(), - [createTxn]); - }, - - /** - * Returns a childItems-transactions array representing the URIList with - * which the dialog has been opened. - */ - _getTransactionsForURIList: function BPP__getTransactionsForURIList() { - var transactions = []; - for (var i = 0; i < this._URIs.length; ++i) { - var uri = this._URIs[i]; - var title = this._getURITitleFromHistory(uri); - var createTxn = new PlacesCreateBookmarkTransaction(uri, -1, - PlacesUtils.bookmarks.DEFAULT_INDEX, - title); - transactions.push(createTxn); - } - return transactions; - }, - - /** - * Returns a transaction for creating a new folder item representing the - * various fields and opening arguments of the dialog. - */ - _getCreateNewFolderTransaction: - function BPP__getCreateNewFolderTransaction(aContainer, aIndex) { - var annotations = []; - var childItemsTransactions; - if (this._URIs.length) - childItemsTransactions = this._getTransactionsForURIList(); - - if (this._description) - annotations.push(this._getDescriptionAnnotation(this._description)); - - return new PlacesCreateFolderTransaction(this._title, aContainer, - aIndex, annotations, - childItemsTransactions); - }, - - /** - * Returns a transaction for creating a new live-bookmark item representing - * the various fields and opening arguments of the dialog. - */ - _getCreateNewLivemarkTransaction: - function BPP__getCreateNewLivemarkTransaction(aContainer, aIndex) { - return new PlacesCreateLivemarkTransaction(this._feedURI, this._siteURI, - this._title, - aContainer, aIndex); - }, - - /** - * Dialog-accept code-path for creating a new item (any type) - */ - _createNewItem: Task.async(function* BPP__getCreateItemTransaction() { - var [container, index] = this._getInsertionPointDetails(); - var txn; - - switch (this._itemType) { - case BOOKMARK_FOLDER: - txn = this._getCreateNewFolderTransaction(container, index); - break; - case LIVEMARK_CONTAINER: - txn = this._getCreateNewLivemarkTransaction(container, index); - break; - default: // BOOKMARK_ITEM - txn = this._getCreateNewBookmarkTransaction(container, index); - } - - PlacesUtils.transactionManager.doTransaction(txn); - // This is a temporary hack until we use PlacesTransactions.jsm - if (txn._promise) { - yield txn._promise; - } - - let folderGuid = yield PlacesUtils.promiseItemGuid(container); - let bm = yield PlacesUtils.bookmarks.fetch({ - parentGuid: folderGuid, - index: index - }); - this._itemId = yield PlacesUtils.promiseItemId(bm.guid); - }) -}; diff --git a/components/places/content/bookmarkProperties.xul b/components/places/content/bookmarkProperties.xul deleted file mode 100644 index 2c04f8b..0000000 --- a/components/places/content/bookmarkProperties.xul +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0"?> - -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<?xml-stylesheet href="chrome://global/skin/"?> -<?xml-stylesheet href="chrome://browser/skin/places/editBookmarkOverlay.css"?> -<?xml-stylesheet href="chrome://browser/skin/places/places.css"?> -<?xml-stylesheet href="chrome://browser/content/places/places.css"?> - -<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?> -<?xul-overlay href="chrome://browser/content/places/editBookmarkOverlay.xul"?> - -<!DOCTYPE dialog [ - <!ENTITY % editBookmarkOverlayDTD SYSTEM "chrome://browser/locale/places/editBookmarkOverlay.dtd"> - %editBookmarkOverlayDTD; -]> - -<dialog id="bookmarkproperties" - buttons="accept, cancel" - buttoniconaccept="save" - ondialogaccept="BookmarkPropertiesPanel.onDialogAccept();" - ondialogcancel="BookmarkPropertiesPanel.onDialogCancel();" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - onload="BookmarkPropertiesPanel.onDialogLoad();" - onunload="BookmarkPropertiesPanel.onDialogUnload();" - style="min-width: 30em;" - persist="screenX screenY width"> - - <stringbundleset id="stringbundleset"> - <stringbundle id="stringBundle" - src="chrome://browser/locale/places/bookmarkProperties.properties"/> - </stringbundleset> - - <script type="application/javascript" - src="chrome://browser/content/places/editBookmarkOverlay.js"/> - <script type="application/javascript" - src="chrome://browser/content/places/bookmarkProperties.js"/> - -<vbox id="editBookmarkPanelContent"/> - -</dialog> diff --git a/components/places/content/bookmarksPanel.js b/components/places/content/bookmarksPanel.js deleted file mode 100644 index c964bd0..0000000 --- a/components/places/content/bookmarksPanel.js +++ /dev/null @@ -1,25 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -function init() { - document.getElementById("bookmarks-view").place = - "place:queryType=1&folder=" + window.top.PlacesUIUtils.allBookmarksFolderId; -} - -function searchBookmarks(aSearchString) { - var tree = document.getElementById('bookmarks-view'); - if (!aSearchString) - tree.place = tree.place; - else - tree.applyFilter(aSearchString, - [PlacesUtils.bookmarksMenuFolderId, - PlacesUtils.unfiledBookmarksFolderId, - PlacesUtils.toolbarFolderId]); -} - -window.addEventListener("SidebarFocused", - function() - document.getElementById("search-box").focus(), - false); diff --git a/components/places/content/bookmarksPanel.xul b/components/places/content/bookmarksPanel.xul deleted file mode 100644 index 45744bb..0000000 --- a/components/places/content/bookmarksPanel.xul +++ /dev/null @@ -1,55 +0,0 @@ -<?xml version="1.0"?> <!-- -*- Mode: SGML; indent-tabs-mode: nil; -*- --> -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> -<?xml-stylesheet href="chrome://browser/content/places/places.css"?> -<?xml-stylesheet href="chrome://browser/skin/places/places.css"?> -<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?> -<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?> - -<!DOCTYPE page SYSTEM "chrome://browser/locale/places/places.dtd"> - -<page id="bookmarksPanel" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - onload="init();" - onunload="SidebarUtils.setMouseoverURL('');"> - - <script type="application/javascript" - src="chrome://browser/content/bookmarks/sidebarUtils.js"/> - <script type="application/javascript" - src="chrome://browser/content/bookmarks/bookmarksPanel.js"/> - - <commandset id="placesCommands"/> - <commandset id="editMenuCommands"/> - <keyset id="placesCommandKeys"/> - <menupopup id="placesContext"/> - - <!-- Bookmarks and history tooltip --> - <tooltip id="bhTooltip"/> - - <hbox id="sidebar-search-container" align="center"> - <label id="sidebar-search-label" - value="&search.label;" accesskey="&search.accesskey;" control="search-box"/> - <textbox id="search-box" flex="1" type="search" class="compact" - aria-controls="bookmarks-view" - oncommand="searchBookmarks(this.value);"/> - </hbox> - - <tree id="bookmarks-view" class="sidebar-placesTree" type="places" - flex="1" - hidecolumnpicker="true" - context="placesContext" - onkeypress="SidebarUtils.handleTreeKeyPress(event);" - onclick="SidebarUtils.handleTreeClick(this, event, true);" - onmousemove="SidebarUtils.handleTreeMouseMove(event);" - onmouseout="SidebarUtils.setMouseoverURL('');"> - <treecols> - <treecol id="title" flex="1" primary="true" hideheader="true"/> - </treecols> - <treechildren id="bookmarks-view-children" view="bookmarks-view" - class="sidebar-placesTreechildren" flex="1" tooltip="bhTooltip"/> - </tree> -</page> diff --git a/components/places/content/browserPlacesViews.js b/components/places/content/browserPlacesViews.js deleted file mode 100644 index 8b90dd2..0000000 --- a/components/places/content/browserPlacesViews.js +++ /dev/null @@ -1,1754 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); -Components.utils.import("resource://gre/modules/Services.jsm"); - -/** - * The base view implements everything that's common to the toolbar and - * menu views. - */ -function PlacesViewBase(aPlace) { - this.place = aPlace; - this._controller = new PlacesController(this); - this._viewElt.controllers.appendController(this._controller); -} - -PlacesViewBase.prototype = { - // The xul element that holds the entire view. - _viewElt: null, - get viewElt() this._viewElt, - - get associatedElement() this._viewElt, - - get controllers() this._viewElt.controllers, - - // The xul element that represents the root container. - _rootElt: null, - - // Set to true for views that are represented by native widgets (i.e. - // the native mac menu). - _nativeView: false, - - QueryInterface: XPCOMUtils.generateQI( - [Components.interfaces.nsINavHistoryResultObserver, - Components.interfaces.nsISupportsWeakReference]), - - _place: "", - get place() this._place, - set place(val) { - this._place = val; - - let history = PlacesUtils.history; - let queries = { }, options = { }; - history.queryStringToQueries(val, queries, { }, options); - if (!queries.value.length) - queries.value = [history.getNewQuery()]; - - let result = history.executeQueries(queries.value, queries.value.length, - options.value); - result.addObserver(this, false); - return val; - }, - - _result: null, - get result() this._result, - set result(val) { - if (this._result == val) - return val; - - if (this._result) { - this._result.removeObserver(this); - this._resultNode.containerOpen = false; - } - - if (this._rootElt.localName == "menupopup") - this._rootElt._built = false; - - this._result = val; - if (val) { - this._resultNode = val.root; - this._rootElt._placesNode = this._resultNode; - this._domNodes = new Map(); - this._domNodes.set(this._resultNode, this._rootElt); - - // This calls _rebuild through invalidateContainer. - this._resultNode.containerOpen = true; - } - else { - this._resultNode = null; - delete this._domNodes; - } - - return val; - }, - - /** - * Gets the DOM node used for the given places node. - * - * @param aPlacesNode - * a places result node. - * @throws if there is no DOM node set for aPlacesNode. - */ - _getDOMNodeForPlacesNode: - function PVB__getDOMNodeForPlacesNode(aPlacesNode) { - let node = this._domNodes.get(aPlacesNode, null); - if (!node) { - throw new Error("No DOM node set for aPlacesNode.\nnode.type: " + - aPlacesNode.type + ". node.parent: " + aPlacesNode); - } - return node; - }, - - get controller() this._controller, - - get selType() "single", - selectItems: function() { }, - selectAll: function() { }, - - get selectedNode() { - if (this._contextMenuShown) { - let anchor = this._contextMenuShown.triggerNode; - if (!anchor) - return null; - - if (anchor._placesNode) - return this._rootElt == anchor ? null : anchor._placesNode; - - anchor = anchor.parentNode; - return this._rootElt == anchor ? null : (anchor._placesNode || null); - } - return null; - }, - - get hasSelection() this.selectedNode != null, - - get selectedNodes() { - let selectedNode = this.selectedNode; - return selectedNode ? [selectedNode] : []; - }, - - get removableSelectionRanges() { - // On static content the current selectedNode would be the selection's - // parent node. We don't want to allow removing a node when the - // selection is not explicit. - if (document.popupNode && - (document.popupNode == "menupopup" || !document.popupNode._placesNode)) - return []; - - return [this.selectedNodes]; - }, - - get draggableSelection() [this._draggedElt], - - get insertionPoint() { - // There is no insertion point for history queries, so bail out now and - // save a lot of work when updating commands. - let resultNode = this._resultNode; - if (PlacesUtils.nodeIsQuery(resultNode) && - PlacesUtils.asQuery(resultNode).queryOptions.queryType == - Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) - return null; - - // By default, the insertion point is at the top level, at the end. - let index = PlacesUtils.bookmarks.DEFAULT_INDEX; - let container = this._resultNode; - let orientation = Ci.nsITreeView.DROP_BEFORE; - let isTag = false; - - let selectedNode = this.selectedNode; - if (selectedNode) { - let popup = document.popupNode; - if (!popup._placesNode || popup._placesNode == this._resultNode || - popup._placesNode.itemId == -1) { - // If a static menuitem is selected, or if the root node is selected, - // the insertion point is inside the folder, at the end. - container = selectedNode; - orientation = Ci.nsITreeView.DROP_ON; - } - else { - // In all other cases the insertion point is before that node. - container = selectedNode.parent; - index = container.getChildIndex(selectedNode); - isTag = PlacesUtils.nodeIsTagQuery(container); - } - } - - if (PlacesControllerDragHelper.disallowInsertion(container)) - return null; - - return new InsertionPoint(PlacesUtils.getConcreteItemId(container), - index, orientation, isTag); - }, - - buildContextMenu: function PVB_buildContextMenu(aPopup) { - this._contextMenuShown = aPopup; - window.updateCommands("places"); - return this.controller.buildContextMenu(aPopup); - }, - - destroyContextMenu: function PVB_destroyContextMenu(aPopup) { - this._contextMenuShown = null; - }, - - _cleanPopup: function PVB_cleanPopup(aPopup, aDelay) { - // Remove Places nodes from the popup. - let child = aPopup._startMarker; - while (child.nextSibling != aPopup._endMarker) { - let sibling = child.nextSibling; - if (sibling._placesNode && !aDelay) { - aPopup.removeChild(sibling); - } - else if (sibling._placesNode && aDelay) { - // HACK (bug 733419): the popups originating from the OS X native - // menubar don't live-update while open, thus we don't clean it - // until the next popupshowing, to avoid zombie menuitems. - if (!aPopup._delayedRemovals) - aPopup._delayedRemovals = []; - aPopup._delayedRemovals.push(sibling); - child = child.nextSibling; - } - else { - child = child.nextSibling; - } - } - }, - - _rebuildPopup: function PVB__rebuildPopup(aPopup) { - let resultNode = aPopup._placesNode; - if (!resultNode.containerOpen) - return; - - if (this.controller.hasCachedLivemarkInfo(resultNode)) { - this._setEmptyPopupStatus(aPopup, false); - aPopup._built = true; - this._populateLivemarkPopup(aPopup); - return; - } - - this._cleanPopup(aPopup); - - let cc = resultNode.childCount; - if (cc > 0) { - this._setEmptyPopupStatus(aPopup, false); - - for (let i = 0; i < cc; ++i) { - let child = resultNode.getChild(i); - this._insertNewItemToPopup(child, aPopup, null); - } - } - else { - this._setEmptyPopupStatus(aPopup, true); - } - aPopup._built = true; - }, - - _removeChild: function PVB__removeChild(aChild) { - // If document.popupNode pointed to this child, null it out, - // otherwise controller's command-updating may rely on the removed - // item still being "selected". - if (document.popupNode == aChild) - document.popupNode = null; - - aChild.parentNode.removeChild(aChild); - }, - - _setEmptyPopupStatus: - function PVB__setEmptyPopupStatus(aPopup, aEmpty) { - if (!aPopup._emptyMenuitem) { - let label = PlacesUIUtils.getString("bookmarksMenuEmptyFolder"); - aPopup._emptyMenuitem = document.createElement("menuitem"); - aPopup._emptyMenuitem.setAttribute("label", label); - aPopup._emptyMenuitem.setAttribute("disabled", true); - } - - if (aEmpty) { - aPopup.setAttribute("emptyplacesresult", "true"); - // Don't add the menuitem if there is static content. - if (!aPopup._startMarker.previousSibling && - !aPopup._endMarker.nextSibling) - aPopup.insertBefore(aPopup._emptyMenuitem, aPopup._endMarker); - } - else { - aPopup.removeAttribute("emptyplacesresult"); - try { - aPopup.removeChild(aPopup._emptyMenuitem); - } catch (ex) {} - } - }, - - _createMenuItemForPlacesNode: - function PVB__createMenuItemForPlacesNode(aPlacesNode) { - this._domNodes.delete(aPlacesNode); - - let element; - let type = aPlacesNode.type; - if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) { - element = document.createElement("menuseparator"); - } - else { - let itemId = aPlacesNode.itemId; - if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_URI) { - element = document.createElement("menuitem"); - element.className = "menuitem-iconic bookmark-item menuitem-with-favicon"; - element.setAttribute("scheme", - PlacesUIUtils.guessUrlSchemeForUI(aPlacesNode.uri)); - } - else if (PlacesUtils.containerTypes.indexOf(type) != -1) { - element = document.createElement("menu"); - element.setAttribute("container", "true"); - - if (aPlacesNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY) { - element.setAttribute("query", "true"); - if (PlacesUtils.nodeIsTagQuery(aPlacesNode)) - element.setAttribute("tagContainer", "true"); - else if (PlacesUtils.nodeIsDay(aPlacesNode)) - element.setAttribute("dayContainer", "true"); - else if (PlacesUtils.nodeIsHost(aPlacesNode)) - element.setAttribute("hostContainer", "true"); - } - else if (itemId != -1) { - PlacesUtils.livemarks.getLivemark({ id: itemId }) - .then(aLivemark => { - element.setAttribute("livemark", "true"); -#ifdef XP_MACOSX - // OS X native menubar doesn't track list-style-images since - // it doesn't have a frame (bug 733415). Thus enforce updating. - element.setAttribute("image", ""); - element.removeAttribute("image"); -#endif - this.controller.cacheLivemarkInfo(aPlacesNode, aLivemark); - }, () => undefined); - } - - let popup = document.createElement("menupopup"); - popup._placesNode = PlacesUtils.asContainer(aPlacesNode); - - if (!this._nativeView) { - popup.setAttribute("placespopup", "true"); - } - -#ifdef XP_MACOSX - // No context menu on mac. - popup.setAttribute("context", "placesContext"); -#endif - element.appendChild(popup); - element.className = "menu-iconic bookmark-item"; - - this._domNodes.set(aPlacesNode, popup); - } - else - throw "Unexpected node"; - - element.setAttribute("label", PlacesUIUtils.getBestTitle(aPlacesNode)); - - let icon = aPlacesNode.icon; - if (icon) - element.setAttribute("image", - PlacesUIUtils.getImageURLForResolution(window, icon)); - } - - element._placesNode = aPlacesNode; - if (!this._domNodes.has(aPlacesNode)) - this._domNodes.set(aPlacesNode, element); - - return element; - }, - - _insertNewItemToPopup: - function PVB__insertNewItemToPopup(aNewChild, aPopup, aBefore) { - let element = this._createMenuItemForPlacesNode(aNewChild); - let before = aBefore || aPopup._endMarker; - aPopup.insertBefore(element, before); - return element; - }, - - _setLivemarkSiteURIMenuItem: - function PVB__setLivemarkSiteURIMenuItem(aPopup) { - let livemarkInfo = this.controller.getCachedLivemarkInfo(aPopup._placesNode); - let siteUrl = livemarkInfo && livemarkInfo.siteURI ? - livemarkInfo.siteURI.spec : null; - if (!siteUrl && aPopup._siteURIMenuitem) { - aPopup.removeChild(aPopup._siteURIMenuitem); - aPopup._siteURIMenuitem = null; - aPopup.removeChild(aPopup._siteURIMenuseparator); - aPopup._siteURIMenuseparator = null; - } - else if (siteUrl && !aPopup._siteURIMenuitem) { - // Add "Open (Feed Name)" menuitem. - aPopup._siteURIMenuitem = document.createElement("menuitem"); - aPopup._siteURIMenuitem.className = "openlivemarksite-menuitem"; - aPopup._siteURIMenuitem.setAttribute("targetURI", siteUrl); - aPopup._siteURIMenuitem.setAttribute("oncommand", - "openUILink(this.getAttribute('targetURI'), event);"); - - // If a user middle-clicks this item we serve the oncommand event. - // We are using checkForMiddleClick because of Bug 246720. - // Note: stopPropagation is needed to avoid serving middle-click - // with BT_onClick that would open all items in tabs. - aPopup._siteURIMenuitem.setAttribute("onclick", - "checkForMiddleClick(this, event); event.stopPropagation();"); - let label = - PlacesUIUtils.getFormattedString("menuOpenLivemarkOrigin.label", - [aPopup.parentNode.getAttribute("label")]) - aPopup._siteURIMenuitem.setAttribute("label", label); - aPopup.insertBefore(aPopup._siteURIMenuitem, aPopup._startMarker); - - aPopup._siteURIMenuseparator = document.createElement("menuseparator"); - aPopup.insertBefore(aPopup._siteURIMenuseparator, aPopup._startMarker); - } - }, - - /** - * Add, update or remove the livemark status menuitem. - * @param aPopup - * The livemark container popup - * @param aStatus - * The livemark status - */ - _setLivemarkStatusMenuItem: - function PVB_setLivemarkStatusMenuItem(aPopup, aStatus) { - let statusMenuitem = aPopup._statusMenuitem; - if (!statusMenuitem) { - // Create the status menuitem and cache it in the popup object. - statusMenuitem = document.createElement("menuitem"); - statusMenuitem.className = "livemarkstatus-menuitem"; - statusMenuitem.setAttribute("disabled", true); - aPopup._statusMenuitem = statusMenuitem; - } - - if (aStatus == Ci.mozILivemark.STATUS_LOADING || - aStatus == Ci.mozILivemark.STATUS_FAILED) { - // Status has changed, update the cached status menuitem. - let stringId = aStatus == Ci.mozILivemark.STATUS_LOADING ? - "bookmarksLivemarkLoading" : "bookmarksLivemarkFailed"; - statusMenuitem.setAttribute("label", PlacesUIUtils.getString(stringId)); - if (aPopup._startMarker.nextSibling != statusMenuitem) - aPopup.insertBefore(statusMenuitem, aPopup._startMarker.nextSibling); - } - else { - // The livemark has finished loading. - if (aPopup._statusMenuitem.parentNode == aPopup) - aPopup.removeChild(aPopup._statusMenuitem); - } - }, - - toggleCutNode: function PVB_toggleCutNode(aPlacesNode, aValue) { - let elt = this._getDOMNodeForPlacesNode(aPlacesNode); - - // We may get the popup for menus, but we need the menu itself. - if (elt.localName == "menupopup") - elt = elt.parentNode; - if (aValue) - elt.setAttribute("cutting", "true"); - else - elt.removeAttribute("cutting"); - }, - - nodeURIChanged: function PVB_nodeURIChanged(aPlacesNode, aURIString) { - let elt = this._getDOMNodeForPlacesNode(aPlacesNode); - - // Here we need the <menu>. - if (elt.localName == "menupopup") - elt = elt.parentNode; - - elt.setAttribute("scheme", PlacesUIUtils.guessUrlSchemeForUI(aURIString)); - }, - - nodeIconChanged: function PVB_nodeIconChanged(aPlacesNode) { - let elt = this._getDOMNodeForPlacesNode(aPlacesNode); - - // There's no UI representation for the root node, thus there's nothing to - // be done when the icon changes. - if (elt == this._rootElt) - return; - - // Here we need the <menu>. - if (elt.localName == "menupopup") - elt = elt.parentNode; - - let icon = aPlacesNode.icon; - if (!icon) - elt.removeAttribute("image"); - else if (icon != elt.getAttribute("image")) - elt.setAttribute("image", - PlacesUIUtils.getImageURLForResolution(window, icon)); - }, - - nodeAnnotationChanged: - function PVB_nodeAnnotationChanged(aPlacesNode, aAnno) { - let elt = this._getDOMNodeForPlacesNode(aPlacesNode); - - // All livemarks have a feedURI, so use it as our indicator of a livemark - // being modified. - if (aAnno == PlacesUtils.LMANNO_FEEDURI) { - let menu = elt.parentNode; - if (!menu.hasAttribute("livemark")) { - menu.setAttribute("livemark", "true"); -#ifdef XP_MACOSX - // OS X native menubar doesn't track list-style-images since - // it doesn't have a frame (bug 733415). Thus enforce updating. - menu.setAttribute("image", ""); - menu.removeAttribute("image"); -#endif - } - - PlacesUtils.livemarks.getLivemark({ id: aPlacesNode.itemId }) - .then(aLivemark => { - // Controller will use this to build the meta data for the node. - this.controller.cacheLivemarkInfo(aPlacesNode, aLivemark); - this.invalidateContainer(aPlacesNode); - }, () => undefined); - } - }, - - nodeTitleChanged: - function PVB_nodeTitleChanged(aPlacesNode, aNewTitle) { - let elt = this._getDOMNodeForPlacesNode(aPlacesNode); - - // There's no UI representation for the root node, thus there's - // nothing to be done when the title changes. - if (elt == this._rootElt) - return; - - // Here we need the <menu>. - if (elt.localName == "menupopup") - elt = elt.parentNode; - - if (!aNewTitle && elt.localName != "toolbarbutton") { - // Many users consider toolbars as shortcuts containers, so explicitly - // allow empty labels on toolbarbuttons. For any other element try to be - // smarter, guessing a title from the uri. - elt.setAttribute("label", PlacesUIUtils.getBestTitle(aPlacesNode)); - } - else { - elt.setAttribute("label", aNewTitle); - } - }, - - nodeRemoved: - function PVB_nodeRemoved(aParentPlacesNode, aPlacesNode, aIndex) { - let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode); - let elt = this._getDOMNodeForPlacesNode(aPlacesNode); - - // Here we need the <menu>. - if (elt.localName == "menupopup") - elt = elt.parentNode; - - if (parentElt._built) { - parentElt.removeChild(elt); - - // Figure out if we need to show the "<Empty>" menu-item. - // TODO Bug 517701: This doesn't seem to handle the case of an empty - // root. - if (parentElt._startMarker.nextSibling == parentElt._endMarker) - this._setEmptyPopupStatus(parentElt, true); - } - }, - - nodeHistoryDetailsChanged: - function PVB_nodeHistoryDetailsChanged(aPlacesNode, aTime, aCount) { - if (aPlacesNode.parent && - this.controller.hasCachedLivemarkInfo(aPlacesNode.parent)) { - // Find the node in the parent. - let popup = this._getDOMNodeForPlacesNode(aPlacesNode.parent); - for (let child = popup._startMarker.nextSibling; - child != popup._endMarker; - child = child.nextSibling) { - if (child._placesNode && child._placesNode.uri == aPlacesNode.uri) { - if (aCount) - child.setAttribute("visited", "true"); - else - child.removeAttribute("visited"); - break; - } - } - } - }, - - nodeTagsChanged: function() { }, - nodeDateAddedChanged: function() { }, - nodeLastModifiedChanged: function() { }, - nodeKeywordChanged: function() { }, - sortingChanged: function() { }, - batching: function() { }, - - nodeInserted: - function PVB_nodeInserted(aParentPlacesNode, aPlacesNode, aIndex) { - let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode); - if (!parentElt._built) - return; - - let index = Array.indexOf(parentElt.childNodes, parentElt._startMarker) + - aIndex + 1; - this._insertNewItemToPopup(aPlacesNode, parentElt, - parentElt.childNodes[index]); - this._setEmptyPopupStatus(parentElt, false); - }, - - nodeMoved: - function PBV_nodeMoved(aPlacesNode, - aOldParentPlacesNode, aOldIndex, - aNewParentPlacesNode, aNewIndex) { - // Note: the current implementation of moveItem does not actually - // use this notification when the item in question is moved from one - // folder to another. Instead, it calls nodeRemoved and nodeInserted - // for the two folders. Thus, we can assume old-parent == new-parent. - let elt = this._getDOMNodeForPlacesNode(aPlacesNode); - - // Here we need the <menu>. - if (elt.localName == "menupopup") - elt = elt.parentNode; - - // If our root node is a folder, it might be moved. There's nothing - // we need to do in that case. - if (elt == this._rootElt) - return; - - let parentElt = this._getDOMNodeForPlacesNode(aNewParentPlacesNode); - if (parentElt._built) { - // Move the node. - parentElt.removeChild(elt); - let index = Array.indexOf(parentElt.childNodes, parentElt._startMarker) + - aNewIndex + 1; - parentElt.insertBefore(elt, parentElt.childNodes[index]); - } - }, - - containerStateChanged: - function PVB_containerStateChanged(aPlacesNode, aOldState, aNewState) { - if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED || - aNewState == Ci.nsINavHistoryContainerResultNode.STATE_CLOSED) { - this.invalidateContainer(aPlacesNode); - - if (PlacesUtils.nodeIsFolder(aPlacesNode)) { - let queryOptions = PlacesUtils.asQuery(this._result.root).queryOptions; - if (queryOptions.excludeItems) { - return; - } - - PlacesUtils.livemarks.getLivemark({ id: aPlacesNode.itemId }) - .then(aLivemark => { - let shouldInvalidate = - !this.controller.hasCachedLivemarkInfo(aPlacesNode); - this.controller.cacheLivemarkInfo(aPlacesNode, aLivemark); - if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED) { - aLivemark.registerForUpdates(aPlacesNode, this); - // Prioritize the current livemark. - aLivemark.reload(); - PlacesUtils.livemarks.reloadLivemarks(); - if (shouldInvalidate) - this.invalidateContainer(aPlacesNode); - } - else { - aLivemark.unregisterForUpdates(aPlacesNode); - } - }, () => undefined); - } - } - }, - - _populateLivemarkPopup: function PVB__populateLivemarkPopup(aPopup) - { - this._setLivemarkSiteURIMenuItem(aPopup); - // Show the loading status only if there are no entries yet. - if (aPopup._startMarker.nextSibling == aPopup._endMarker) - this._setLivemarkStatusMenuItem(aPopup, Ci.mozILivemark.STATUS_LOADING); - - PlacesUtils.livemarks.getLivemark({ id: aPopup._placesNode.itemId }) - .then(aLivemark => { - let placesNode = aPopup._placesNode; - if (!placesNode.containerOpen) - return; - - if (aLivemark.status != Ci.mozILivemark.STATUS_LOADING) - this._setLivemarkStatusMenuItem(aPopup, aLivemark.status); - this._cleanPopup(aPopup, - this._nativeView && aPopup.parentNode.hasAttribute("open")); - - let children = aLivemark.getNodesForContainer(placesNode); - for (let i = 0; i < children.length; i++) { - let child = children[i]; - this.nodeInserted(placesNode, child, i); - if (child.accessCount) - this._getDOMNodeForPlacesNode(child).setAttribute("visited", true); - else - this._getDOMNodeForPlacesNode(child).removeAttribute("visited"); - } - }, Components.utils.reportError); - }, - - invalidateContainer: function PVB_invalidateContainer(aPlacesNode) { - let elt = this._getDOMNodeForPlacesNode(aPlacesNode); - elt._built = false; - - // If the menupopup is open we should live-update it. - if (elt.parentNode.open) - this._rebuildPopup(elt); - }, - - uninit: function PVB_uninit() { - if (this._result) { - this._result.removeObserver(this); - this._resultNode.containerOpen = false; - this._resultNode = null; - this._result = null; - } - - if (this._controller) { - this._controller.terminate(); - // Removing the controller will fail if it is already no longer there. - // This can happen if the view element was removed/reinserted without - // our knowledge. There is no way to check for that having happened - // without the possibility of an exception. :-( - try { - this._viewElt.controllers.removeController(this._controller); - } catch (ex) { - } finally { - this._controller = null; - } - } - - delete this._viewElt._placesView; - }, - - get isRTL() { - if ("_isRTL" in this) - return this._isRTL; - - return this._isRTL = document.defaultView - .getComputedStyle(this.viewElt, "") - .direction == "rtl"; - }, - - get ownerWindow() window, - - /** - * Adds an "Open All in Tabs" menuitem to the bottom of the popup. - * @param aPopup - * a Places popup. - */ - _mayAddCommandsItems: function PVB__mayAddCommandsItems(aPopup) { - // The command items are never added to the root popup. - if (aPopup == this._rootElt) - return; - - let hasMultipleURIs = false; - - // Check if the popup contains at least 2 menuitems with places nodes. - // We don't currently support opening multiple uri nodes when they are not - // populated by the result. - if (aPopup._placesNode.childCount > 0) { - let currentChild = aPopup.firstChild; - let numURINodes = 0; - while (currentChild) { - if (currentChild.localName == "menuitem" && currentChild._placesNode) { - if (++numURINodes == 2) - break; - } - currentChild = currentChild.nextSibling; - } - hasMultipleURIs = numURINodes > 1; - } - - let isLiveMark = false; - if (this.controller.hasCachedLivemarkInfo(aPopup._placesNode)) { - hasMultipleURIs = true; - isLiveMark = true; - } - - if (!hasMultipleURIs) { - // We don't have to show any option. - if (aPopup._endOptOpenAllInTabs) { - aPopup.removeChild(aPopup._endOptOpenAllInTabs); - aPopup._endOptOpenAllInTabs = null; - - aPopup.removeChild(aPopup._endOptSeparator); - aPopup._endOptSeparator = null; - } - } - else if (!aPopup._endOptOpenAllInTabs) { - // Create a separator before options. - aPopup._endOptSeparator = document.createElement("menuseparator"); - aPopup._endOptSeparator.className = "bookmarks-actions-menuseparator"; - aPopup.appendChild(aPopup._endOptSeparator); - - // Add the "Open All in Tabs" menuitem. - aPopup._endOptOpenAllInTabs = document.createElement("menuitem"); - aPopup._endOptOpenAllInTabs.className = "openintabs-menuitem"; - if (isLiveMark) { - aPopup._endOptOpenAllInTabs.setAttribute("oncommand", - "PlacesUIUtils.openLiveMarkNodesInTabs(this.parentNode._placesNode, event, " + - "PlacesUIUtils.getViewForNode(this));"); - } else { - aPopup._endOptOpenAllInTabs.setAttribute("oncommand", - "PlacesUIUtils.openContainerNodeInTabs(this.parentNode._placesNode, event, " + - "PlacesUIUtils.getViewForNode(this));"); - } - aPopup._endOptOpenAllInTabs.setAttribute("onclick", - "checkForMiddleClick(this, event); event.stopPropagation();"); - aPopup._endOptOpenAllInTabs.setAttribute("label", - gNavigatorBundle.getString("menuOpenAllInTabs.label")); - aPopup.appendChild(aPopup._endOptOpenAllInTabs); - } - }, - - _ensureMarkers: function PVB__ensureMarkers(aPopup) { - if (aPopup._startMarker) - return; - - // _startMarker is an hidden menuseparator that lives before places nodes. - aPopup._startMarker = document.createElement("menuseparator"); - aPopup._startMarker.hidden = true; - aPopup.insertBefore(aPopup._startMarker, aPopup.firstChild); - - // _endMarker is an hidden menuseparator that lives after places nodes. - aPopup._endMarker = document.createElement("menuseparator"); - aPopup._endMarker.hidden = true; - aPopup.appendChild(aPopup._endMarker); - - // Move the markers to the right position. - let firstNonStaticNodeFound = false; - for (let i = 0; i < aPopup.childNodes.length; i++) { - let child = aPopup.childNodes[i]; - // Menus that have static content at the end, but are initially empty, - // use a special "builder" attribute to figure out where to start - // inserting places nodes. - if (child.getAttribute("builder") == "end") { - aPopup.insertBefore(aPopup._endMarker, child); - break; - } - - if (child._placesNode && !firstNonStaticNodeFound) { - firstNonStaticNodeFound = true; - aPopup.insertBefore(aPopup._startMarker, child); - } - } - if (!firstNonStaticNodeFound) { - aPopup.insertBefore(aPopup._startMarker, aPopup._endMarker); - } - }, - - _onPopupShowing: function PVB__onPopupShowing(aEvent) { - // Avoid handling popupshowing of inner views. - let popup = aEvent.originalTarget; - - this._ensureMarkers(popup); - - // Remove any delayed element, see _cleanPopup for details. - if ("_delayedRemovals" in popup) { - while (popup._delayedRemovals.length > 0) { - popup.removeChild(popup._delayedRemovals.shift()); - } - } - - if (popup._placesNode && PlacesUIUtils.getViewForNode(popup) == this) { - if (!popup._placesNode.containerOpen) - popup._placesNode.containerOpen = true; - if (!popup._built) - this._rebuildPopup(popup); - - this._mayAddCommandsItems(popup); - } - }, - - _addEventListeners: - function PVB__addEventListeners(aObject, aEventNames, aCapturing) { - for (let i = 0; i < aEventNames.length; i++) { - aObject.addEventListener(aEventNames[i], this, aCapturing); - } - }, - - _removeEventListeners: - function PVB__removeEventListeners(aObject, aEventNames, aCapturing) { - for (let i = 0; i < aEventNames.length; i++) { - aObject.removeEventListener(aEventNames[i], this, aCapturing); - } - }, -}; - -function PlacesToolbar(aPlace) { - let startTime = Date.now(); - // Add some smart getters for our elements. - let thisView = this; - [ - ["_viewElt", "PlacesToolbar"], - ["_rootElt", "PlacesToolbarItems"], - ["_dropIndicator", "PlacesToolbarDropIndicator"], - ["_chevron", "PlacesChevron"], - ["_chevronPopup", "PlacesChevronPopup"] - ].forEach(function (elementGlobal) { - let [name, id] = elementGlobal; - thisView.__defineGetter__(name, function () { - let element = document.getElementById(id); - if (!element) - return null; - - delete thisView[name]; - return thisView[name] = element; - }); - }); - - this._viewElt._placesView = this; - - this._addEventListeners(this._viewElt, this._cbEvents, false); - this._addEventListeners(this._rootElt, ["popupshowing", "popuphidden"], true); - this._addEventListeners(this._rootElt, ["overflow", "underflow"], true); - this._addEventListeners(window, ["resize", "unload"], false); - - // If personal-bookmarks has been dragged to the tabs toolbar, - // we have to track addition and removals of tabs, to properly - // recalculate the available space for bookmarks. - // TODO (bug 734730): Use a performant mutation listener when available. - if (this._viewElt.parentNode.parentNode == document.getElementById("TabsToolbar")) { - this._addEventListeners(gBrowser.tabContainer, ["TabOpen", "TabClose"], false); - } - - PlacesViewBase.call(this, aPlace); -} - -PlacesToolbar.prototype = { - __proto__: PlacesViewBase.prototype, - - _cbEvents: ["dragstart", "dragover", "dragexit", "dragend", "drop", - "mousemove", "mouseover", "mouseout"], - - QueryInterface: function PT_QueryInterface(aIID) { - if (aIID.equals(Ci.nsIDOMEventListener) || - aIID.equals(Ci.nsITimerCallback)) - return this; - - return PlacesViewBase.prototype.QueryInterface.apply(this, arguments); - }, - - uninit: function PT_uninit() { - this._removeEventListeners(this._viewElt, this._cbEvents, false); - this._removeEventListeners(this._rootElt, ["popupshowing", "popuphidden"], - true); - this._removeEventListeners(this._rootElt, ["overflow", "underflow"], true); - this._removeEventListeners(window, ["resize", "unload"], false); - this._removeEventListeners(gBrowser.tabContainer, ["TabOpen", "TabClose"], false); - - PlacesViewBase.prototype.uninit.apply(this, arguments); - }, - - _openedMenuButton: null, - _allowPopupShowing: true, - - _rebuild: function PT__rebuild() { - // Clear out references to existing nodes, since they will be removed - // and re-added. - if (this._overFolder.elt) - this._clearOverFolder(); - - this._openedMenuButton = null; - while (this._rootElt.hasChildNodes()) { - this._rootElt.removeChild(this._rootElt.firstChild); - } - - let cc = this._resultNode.childCount; - for (let i = 0; i < cc; ++i) { - this._insertNewItem(this._resultNode.getChild(i), null); - } - - if (this._chevronPopup.hasAttribute("type")) { - // Chevron has already been initialized, but since we are forcing - // a rebuild of the toolbar, it has to be rebuilt. - // Otherwise, it will be initialized when the toolbar overflows. - this._chevronPopup.place = this.place; - } - }, - - _insertNewItem: - function PT__insertNewItem(aChild, aBefore) { - this._domNodes.delete(aChild); - - let type = aChild.type; - let button; - if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) { - button = document.createElement("toolbarseparator"); - } - else { - button = document.createElement("toolbarbutton"); - button.className = "bookmark-item"; - button.setAttribute("label", aChild.title || ""); - let icon = aChild.icon; - if (icon) - button.setAttribute("image", - PlacesUIUtils.getImageURLForResolution(window, icon)); - - if (PlacesUtils.containerTypes.indexOf(type) != -1) { - button.setAttribute("type", "menu"); - button.setAttribute("container", "true"); - - if (PlacesUtils.nodeIsQuery(aChild)) { - button.setAttribute("query", "true"); - if (PlacesUtils.nodeIsTagQuery(aChild)) - button.setAttribute("tagContainer", "true"); - } - else if (PlacesUtils.nodeIsFolder(aChild)) { - PlacesUtils.livemarks.getLivemark({ id: aChild.itemId }) - .then(aLivemark => { - button.setAttribute("livemark", "true"); - this.controller.cacheLivemarkInfo(aChild, aLivemark); - }, () => undefined); - } - - let popup = document.createElement("menupopup"); - popup.setAttribute("placespopup", "true"); - button.appendChild(popup); - popup._placesNode = PlacesUtils.asContainer(aChild); -#ifndef XP_MACOSX - popup.setAttribute("context", "placesContext"); -#endif - - this._domNodes.set(aChild, popup); - } - else if (PlacesUtils.nodeIsURI(aChild)) { - button.setAttribute("scheme", - PlacesUIUtils.guessUrlSchemeForUI(aChild.uri)); - } - } - - button._placesNode = aChild; - if (!this._domNodes.has(aChild)) - this._domNodes.set(aChild, button); - - if (aBefore) - this._rootElt.insertBefore(button, aBefore); - else - this._rootElt.appendChild(button); - }, - - _updateChevronPopupNodesVisibility: - function PT__updateChevronPopupNodesVisibility() { - for (let i = 0, node = this._chevronPopup._startMarker.nextSibling; - node != this._chevronPopup._endMarker; - i++, node = node.nextSibling) { - node.hidden = this._rootElt.childNodes[i].style.visibility != "hidden"; - } - }, - - _onChevronPopupShowing: - function PT__onChevronPopupShowing(aEvent) { - // Handle popupshowing only for the chevron popup, not for nested ones. - if (aEvent.target != this._chevronPopup) - return; - - if (!this._chevron._placesView) - this._chevron._placesView = new PlacesMenu(aEvent, this.place); - - this._updateChevronPopupNodesVisibility(); - }, - - handleEvent: function PT_handleEvent(aEvent) { - switch (aEvent.type) { - case "unload": - this.uninit(); - break; - case "resize": - // This handler updates nodes visibility in both the toolbar - // and the chevron popup when a window resize does not change - // the overflow status of the toolbar. - this.updateChevron(); - break; - case "overflow": - if (aEvent.target != aEvent.currentTarget) - return; - - // Ignore purely vertical overflows. - if (aEvent.detail == 0) - return; - - // Attach the popup binding to the chevron popup if it has not yet - // been initialized. - if (!this._chevronPopup.hasAttribute("type")) { - this._chevronPopup.setAttribute("place", this.place); - this._chevronPopup.setAttribute("type", "places"); - } - this._chevron.collapsed = false; - this.updateChevron(); - break; - case "underflow": - if (aEvent.target != aEvent.currentTarget) - return; - - // Ignore purely vertical underflows. - if (aEvent.detail == 0) - return; - - this.updateChevron(); - this._chevron.collapsed = true; - break; - case "TabOpen": - case "TabClose": - this.updateChevron(); - break; - case "dragstart": - this._onDragStart(aEvent); - break; - case "dragover": - this._onDragOver(aEvent); - break; - case "dragexit": - this._onDragExit(aEvent); - break; - case "dragend": - this._onDragEnd(aEvent); - break; - case "drop": - this._onDrop(aEvent); - break; - case "mouseover": - this._onMouseOver(aEvent); - break; - case "mousemove": - this._onMouseMove(aEvent); - break; - case "mouseout": - this._onMouseOut(aEvent); - break; - case "popupshowing": - this._onPopupShowing(aEvent); - break; - case "popuphidden": - this._onPopupHidden(aEvent); - break; - default: - throw "Trying to handle unexpected event."; - } - }, - - updateChevron: function PT_updateChevron() { - // If the chevron is collapsed there's nothing to update. - if (this._chevron.collapsed) - return; - - // Update the chevron on a timer. This will avoid repeated work when - // lot of changes happen in a small timeframe. - if (this._updateChevronTimer) - this._updateChevronTimer.cancel(); - - this._updateChevronTimer = this._setTimer(100); - }, - - _updateChevronTimerCallback: function PT__updateChevronTimerCallback() { - let scrollRect = this._rootElt.getBoundingClientRect(); - let childOverflowed = false; - for (let i = 0; i < this._rootElt.childNodes.length; i++) { - let child = this._rootElt.childNodes[i]; - // Once a child overflows, all the next ones will. - if (!childOverflowed) { - let childRect = child.getBoundingClientRect(); - childOverflowed = this.isRTL ? (childRect.left < scrollRect.left) - : (childRect.right > scrollRect.right); - - } - child.style.visibility = childOverflowed ? "hidden" : "visible"; - } - - // We rebuild the chevron on popupShowing, so if it is open - // we must update it. - if (this._chevron.open) - this._updateChevronPopupNodesVisibility(); - }, - - nodeInserted: - function PT_nodeInserted(aParentPlacesNode, aPlacesNode, aIndex) { - let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode); - if (parentElt == this._rootElt) { - let children = this._rootElt.childNodes; - this._insertNewItem(aPlacesNode, - aIndex < children.length ? children[aIndex] : null); - this.updateChevron(); - return; - } - - PlacesViewBase.prototype.nodeInserted.apply(this, arguments); - }, - - nodeRemoved: - function PT_nodeRemoved(aParentPlacesNode, aPlacesNode, aIndex) { - let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode); - let elt = this._getDOMNodeForPlacesNode(aPlacesNode); - - // Here we need the <menu>. - if (elt.localName == "menupopup") - elt = elt.parentNode; - - if (parentElt == this._rootElt) { - this._removeChild(elt); - this.updateChevron(); - return; - } - - PlacesViewBase.prototype.nodeRemoved.apply(this, arguments); - }, - - nodeMoved: - function PT_nodeMoved(aPlacesNode, - aOldParentPlacesNode, aOldIndex, - aNewParentPlacesNode, aNewIndex) { - let parentElt = this._getDOMNodeForPlacesNode(aNewParentPlacesNode); - if (parentElt == this._rootElt) { - // Container is on the toolbar. - - // Move the element. - let elt = this._getDOMNodeForPlacesNode(aPlacesNode); - - // Here we need the <menu>. - if (elt.localName == "menupopup") - elt = elt.parentNode; - - this._removeChild(elt); - this._rootElt.insertBefore(elt, this._rootElt.childNodes[aNewIndex]); - - // The chevron view may get nodeMoved after the toolbar. In such a case, - // we should ensure (by manually swapping menuitems) that the actual nodes - // are in the final position before updateChevron tries to updates their - // visibility, or the chevron may go out of sync. - // Luckily updateChevron runs on a timer, so, by the time it updates - // nodes, the menu has already handled the notification. - - this.updateChevron(); - return; - } - - PlacesViewBase.prototype.nodeMoved.apply(this, arguments); - }, - - nodeAnnotationChanged: - function PT_nodeAnnotationChanged(aPlacesNode, aAnno) { - let elt = this._getDOMNodeForPlacesNode(aPlacesNode); - if (elt == this._rootElt) - return; - - // We're notified for the menupopup, not the containing toolbarbutton. - if (elt.localName == "menupopup") - elt = elt.parentNode; - - if (elt.parentNode == this._rootElt) { - // Node is on the toolbar. - - // All livemarks have a feedURI, so use it as our indicator. - if (aAnno == PlacesUtils.LMANNO_FEEDURI) { - elt.setAttribute("livemark", true); - - PlacesUtils.livemarks.getLivemark({ id: aPlacesNode.itemId }) - .then(aLivemark => { - this.controller.cacheLivemarkInfo(aPlacesNode, aLivemark); - this.invalidateContainer(aPlacesNode); - }, Components.utils.reportError); - } - } - else { - // Node is in a submenu. - PlacesViewBase.prototype.nodeAnnotationChanged.apply(this, arguments); - } - }, - - nodeTitleChanged: function PT_nodeTitleChanged(aPlacesNode, aNewTitle) { - let elt = this._getDOMNodeForPlacesNode(aPlacesNode); - - // There's no UI representation for the root node, thus there's - // nothing to be done when the title changes. - if (elt == this._rootElt) - return; - - PlacesViewBase.prototype.nodeTitleChanged.apply(this, arguments); - - // Here we need the <menu>. - if (elt.localName == "menupopup") - elt = elt.parentNode; - - if (elt.parentNode == this._rootElt) { - // Node is on the toolbar - this.updateChevron(); - } - }, - - invalidateContainer: function PT_invalidateContainer(aPlacesNode) { - let elt = this._getDOMNodeForPlacesNode(aPlacesNode); - if (elt == this._rootElt) { - // Container is the toolbar itself. - this._rebuild(); - return; - } - - PlacesViewBase.prototype.invalidateContainer.apply(this, arguments); - }, - - _overFolder: { elt: null, - openTimer: null, - hoverTime: 350, - closeTimer: null }, - - _clearOverFolder: function PT__clearOverFolder() { - // The mouse is no longer dragging over the stored menubutton. - // Close the menubutton, clear out drag styles, and clear all - // timers for opening/closing it. - if (this._overFolder.elt && this._overFolder.elt.lastChild) { - if (!this._overFolder.elt.lastChild.hasAttribute("dragover")) { - this._overFolder.elt.lastChild.hidePopup(); - } - this._overFolder.elt.removeAttribute("dragover"); - this._overFolder.elt = null; - } - if (this._overFolder.openTimer) { - this._overFolder.openTimer.cancel(); - this._overFolder.openTimer = null; - } - if (this._overFolder.closeTimer) { - this._overFolder.closeTimer.cancel(); - this._overFolder.closeTimer = null; - } - }, - - /** - * This function returns information about where to drop when dragging over - * the toolbar. The returned object has the following properties: - * - ip: the insertion point for the bookmarks service. - * - beforeIndex: child index to drop before, for the drop indicator. - * - folderElt: the folder to drop into, if applicable. - */ - _getDropPoint: function PT__getDropPoint(aEvent) { - let result = this.result; - if (!PlacesUtils.nodeIsFolder(this._resultNode)) - return null; - - let dropPoint = { ip: null, beforeIndex: null, folderElt: null }; - let elt = aEvent.target; - if (elt._placesNode && elt != this._rootElt && - elt.localName != "menupopup") { - let eltRect = elt.getBoundingClientRect(); - let eltIndex = Array.indexOf(this._rootElt.childNodes, elt); - if (PlacesUtils.nodeIsFolder(elt._placesNode) && - !PlacesUIUtils.isContentsReadOnly(elt._placesNode)) { - // This is a folder. - // If we are in the middle of it, drop inside it. - // Otherwise, drop before it, with regards to RTL mode. - let threshold = eltRect.width * 0.25; - if (this.isRTL ? (aEvent.clientX > eltRect.right - threshold) - : (aEvent.clientX < eltRect.left + threshold)) { - // Drop before this folder. - dropPoint.ip = - new InsertionPoint(PlacesUtils.getConcreteItemId(this._resultNode), - eltIndex, Ci.nsITreeView.DROP_BEFORE); - dropPoint.beforeIndex = eltIndex; - } - else if (this.isRTL ? (aEvent.clientX > eltRect.left + threshold) - : (aEvent.clientX < eltRect.right - threshold)) { - // Drop inside this folder. - dropPoint.ip = - new InsertionPoint(PlacesUtils.getConcreteItemId(elt._placesNode), - -1, Ci.nsITreeView.DROP_ON, - PlacesUtils.nodeIsTagQuery(elt._placesNode)); - dropPoint.beforeIndex = eltIndex; - dropPoint.folderElt = elt; - } - else { - // Drop after this folder. - let beforeIndex = - (eltIndex == this._rootElt.childNodes.length - 1) ? - -1 : eltIndex + 1; - - dropPoint.ip = - new InsertionPoint(PlacesUtils.getConcreteItemId(this._resultNode), - beforeIndex, Ci.nsITreeView.DROP_BEFORE); - dropPoint.beforeIndex = beforeIndex; - } - } - else { - // This is a non-folder node or a read-only folder. - // Drop before it with regards to RTL mode. - let threshold = eltRect.width * 0.5; - if (this.isRTL ? (aEvent.clientX > eltRect.left + threshold) - : (aEvent.clientX < eltRect.left + threshold)) { - // Drop before this bookmark. - dropPoint.ip = - new InsertionPoint(PlacesUtils.getConcreteItemId(this._resultNode), - eltIndex, Ci.nsITreeView.DROP_BEFORE); - dropPoint.beforeIndex = eltIndex; - } - else { - // Drop after this bookmark. - let beforeIndex = - eltIndex == this._rootElt.childNodes.length - 1 ? - -1 : eltIndex + 1; - dropPoint.ip = - new InsertionPoint(PlacesUtils.getConcreteItemId(this._resultNode), - beforeIndex, Ci.nsITreeView.DROP_BEFORE); - dropPoint.beforeIndex = beforeIndex; - } - } - } - else { - // We are most likely dragging on the empty area of the - // toolbar, we should drop after the last node. - dropPoint.ip = - new InsertionPoint(PlacesUtils.getConcreteItemId(this._resultNode), - -1, Ci.nsITreeView.DROP_BEFORE); - dropPoint.beforeIndex = -1; - } - - return dropPoint; - }, - - _setTimer: function PT_setTimer(aTime) { - let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - timer.initWithCallback(this, aTime, timer.TYPE_ONE_SHOT); - return timer; - }, - - notify: function PT_notify(aTimer) { - if (aTimer == this._updateChevronTimer) { - this._updateChevronTimer = null; - this._updateChevronTimerCallback(); - } - - // * Timer to turn off indicator bar. - else if (aTimer == this._ibTimer) { - this._dropIndicator.collapsed = true; - this._ibTimer = null; - } - - // * Timer to open a menubutton that's being dragged over. - else if (aTimer == this._overFolder.openTimer) { - // Set the autoopen attribute on the folder's menupopup so that - // the menu will automatically close when the mouse drags off of it. - this._overFolder.elt.lastChild.setAttribute("autoopened", "true"); - this._overFolder.elt.open = true; - this._overFolder.openTimer = null; - } - - // * Timer to close a menubutton that's been dragged off of. - else if (aTimer == this._overFolder.closeTimer) { - // Close the menubutton if we are not dragging over it or one of - // its children. The autoopened attribute will let the menu know to - // close later if the menu is still being dragged over. - let currentPlacesNode = PlacesControllerDragHelper.currentDropTarget; - let inHierarchy = false; - while (currentPlacesNode) { - if (currentPlacesNode == this._rootElt) { - inHierarchy = true; - break; - } - currentPlacesNode = currentPlacesNode.parentNode; - } - // The _clearOverFolder() function will close the menu for - // _overFolder.elt. So null it out if we don't want to close it. - if (inHierarchy) - this._overFolder.elt = null; - - // Clear out the folder and all associated timers. - this._clearOverFolder(); - } - }, - - _onMouseOver: function PT__onMouseOver(aEvent) { - let button = aEvent.target; - if (button.parentNode == this._rootElt && button._placesNode && - PlacesUtils.nodeIsURI(button._placesNode)) - window.XULBrowserWindow.setOverLink(aEvent.target._placesNode.uri, null); - }, - - _onMouseOut: function PT__onMouseOut(aEvent) { - window.XULBrowserWindow.setOverLink("", null); - }, - - _cleanupDragDetails: function PT__cleanupDragDetails() { - // Called on dragend and drop. - PlacesControllerDragHelper.currentDropTarget = null; - this._draggedElt = null; - if (this._ibTimer) - this._ibTimer.cancel(); - - this._dropIndicator.collapsed = true; - }, - - _onDragStart: function PT__onDragStart(aEvent) { - // Sub menus have their own d&d handlers. - let draggedElt = aEvent.target; - if (draggedElt.parentNode != this._rootElt || !draggedElt._placesNode) - return; - - if (draggedElt.localName == "toolbarbutton" && - draggedElt.getAttribute("type") == "menu") { - // If the drag gesture on a container is toward down we open instead - // of dragging. - let translateY = this._cachedMouseMoveEvent.clientY - aEvent.clientY; - let translateX = this._cachedMouseMoveEvent.clientX - aEvent.clientX; - if ((translateY) >= Math.abs(translateX/2)) { - // Don't start the drag. - aEvent.preventDefault(); - // Open the menu. - draggedElt.open = true; - return; - } - - // If the menu is open, close it. - if (draggedElt.open) { - draggedElt.lastChild.hidePopup(); - draggedElt.open = false; - } - } - - // Activate the view and cache the dragged element. - this._draggedElt = draggedElt._placesNode; - this._rootElt.focus(); - - this._controller.setDataTransfer(aEvent); - aEvent.stopPropagation(); - }, - - _onDragOver: function PT__onDragOver(aEvent) { - // Cache the dataTransfer - PlacesControllerDragHelper.currentDropTarget = aEvent.target; - let dt = aEvent.dataTransfer; - - let dropPoint = this._getDropPoint(aEvent); - if (!dropPoint || !dropPoint.ip || - !PlacesControllerDragHelper.canDrop(dropPoint.ip, dt)) { - this._dropIndicator.collapsed = true; - aEvent.stopPropagation(); - return; - } - - if (this._ibTimer) { - this._ibTimer.cancel(); - this._ibTimer = null; - } - - if (dropPoint.folderElt || aEvent.originalTarget == this._chevron) { - // Dropping over a menubutton or chevron button. - // Set styles and timer to open relative menupopup. - let overElt = dropPoint.folderElt || this._chevron; - if (this._overFolder.elt != overElt) { - this._clearOverFolder(); - this._overFolder.elt = overElt; - this._overFolder.openTimer = this._setTimer(this._overFolder.hoverTime); - } - if (!this._overFolder.elt.hasAttribute("dragover")) - this._overFolder.elt.setAttribute("dragover", "true"); - - this._dropIndicator.collapsed = true; - } - else { - // Dragging over a normal toolbarbutton, - // show indicator bar and move it to the appropriate drop point. - let ind = this._dropIndicator; - let halfInd = ind.clientWidth / 2; - let translateX; - if (this.isRTL) { - halfInd = Math.ceil(halfInd); - translateX = 0 - this._rootElt.getBoundingClientRect().right - halfInd; - if (this._rootElt.firstChild) { - if (dropPoint.beforeIndex == -1) - translateX += this._rootElt.lastChild.getBoundingClientRect().left; - else { - translateX += this._rootElt.childNodes[dropPoint.beforeIndex] - .getBoundingClientRect().right; - } - } - } - else { - halfInd = Math.floor(halfInd); - translateX = 0 - this._rootElt.getBoundingClientRect().left + - halfInd; - if (this._rootElt.firstChild) { - if (dropPoint.beforeIndex == -1) - translateX += this._rootElt.lastChild.getBoundingClientRect().right; - else { - translateX += this._rootElt.childNodes[dropPoint.beforeIndex] - .getBoundingClientRect().left; - } - } - } - - ind.style.transform = "translate(" + Math.round(translateX) + "px)"; - ind.style.MozMarginStart = (-ind.clientWidth) + "px"; - ind.collapsed = false; - - // Clear out old folder information. - this._clearOverFolder(); - } - - aEvent.preventDefault(); - aEvent.stopPropagation(); - }, - - _onDrop: function PT__onDrop(aEvent) { - PlacesControllerDragHelper.currentDropTarget = aEvent.target; - - let dropPoint = this._getDropPoint(aEvent); - if (dropPoint && dropPoint.ip) { - PlacesControllerDragHelper.onDrop(dropPoint.ip, aEvent.dataTransfer) - aEvent.preventDefault(); - } - - this._cleanupDragDetails(); - aEvent.stopPropagation(); - }, - - _onDragExit: function PT__onDragExit(aEvent) { - PlacesControllerDragHelper.currentDropTarget = null; - - // Set timer to turn off indicator bar (if we turn it off - // here, dragenter might be called immediately after, creating - // flicker). - if (this._ibTimer) - this._ibTimer.cancel(); - this._ibTimer = this._setTimer(10); - - // If we hovered over a folder, close it now. - if (this._overFolder.elt) - this._overFolder.closeTimer = this._setTimer(this._overFolder.hoverTime); - }, - - _onDragEnd: function PT_onDragEnd(aEvent) { - this._cleanupDragDetails(); - }, - - _onPopupShowing: function PT__onPopupShowing(aEvent) { - if (!this._allowPopupShowing) { - this._allowPopupShowing = true; - aEvent.preventDefault(); - return; - } - - let parent = aEvent.target.parentNode; - if (parent.localName == "toolbarbutton") - this._openedMenuButton = parent; - - PlacesViewBase.prototype._onPopupShowing.apply(this, arguments); - }, - - _onPopupHidden: function PT__onPopupHidden(aEvent) { - let popup = aEvent.target; - let placesNode = popup._placesNode; - // Avoid handling popuphidden of inner views - if (placesNode && PlacesUIUtils.getViewForNode(popup) == this) { - // UI performance: folder queries are cheap, keep the resultnode open - // so we don't rebuild its contents whenever the popup is reopened. - // Though, we want to always close feed containers so their expiration - // status will be checked at next opening. - if (!PlacesUtils.nodeIsFolder(placesNode) || - this.controller.hasCachedLivemarkInfo(placesNode)) { - placesNode.containerOpen = false; - } - } - - let parent = popup.parentNode; - if (parent.localName == "toolbarbutton") { - this._openedMenuButton = null; - // Clear the dragover attribute if present, if we are dragging into a - // folder in the hierachy of current opened popup we don't clear - // this attribute on clearOverFolder. See Notify for closeTimer. - if (parent.hasAttribute("dragover")) - parent.removeAttribute("dragover"); - } - }, - - _onMouseMove: function PT__onMouseMove(aEvent) { - // Used in dragStart to prevent dragging folders when dragging down. - this._cachedMouseMoveEvent = aEvent; - - if (this._openedMenuButton == null || - PlacesControllerDragHelper.getSession()) - return; - - let target = aEvent.originalTarget; - if (this._openedMenuButton != target && - target.localName == "toolbarbutton" && - target.type == "menu") { - this._openedMenuButton.open = false; - target.open = true; - } - } -}; - -/** - * View for Places menus. This object should be created during the first - * popupshowing that's dispatched on the menu. - */ -function PlacesMenu(aPopupShowingEvent, aPlace) { - this._rootElt = aPopupShowingEvent.target; // <menupopup> - this._viewElt = this._rootElt.parentNode; // <menu> - this._viewElt._placesView = this; - this._addEventListeners(this._rootElt, ["popupshowing", "popuphidden"], true); - this._addEventListeners(window, ["unload"], false); - -#ifdef XP_MACOSX - // Must walk up to support views in sub-menus, like Bookmarks Toolbar menu. - for (let elt = this._viewElt.parentNode; elt; elt = elt.parentNode) { - if (elt.localName == "menubar") { - this._nativeView = true; - break; - } - } -#endif - - PlacesViewBase.call(this, aPlace); - this._onPopupShowing(aPopupShowingEvent); -} - -PlacesMenu.prototype = { - __proto__: PlacesViewBase.prototype, - - QueryInterface: function PM_QueryInterface(aIID) { - if (aIID.equals(Ci.nsIDOMEventListener)) - return this; - - return PlacesViewBase.prototype.QueryInterface.apply(this, arguments); - }, - - _removeChild: function PM_removeChild(aChild) { - PlacesViewBase.prototype._removeChild.apply(this, arguments); - }, - - uninit: function PM_uninit() { - this._removeEventListeners(this._rootElt, ["popupshowing", "popuphidden"], - true); - this._removeEventListeners(window, ["unload"], false); - - PlacesViewBase.prototype.uninit.apply(this, arguments); - }, - - handleEvent: function PM_handleEvent(aEvent) { - switch (aEvent.type) { - case "unload": - this.uninit(); - break; - case "popupshowing": - this._onPopupShowing(aEvent); - break; - case "popuphidden": - this._onPopupHidden(aEvent); - break; - } - }, - - _onPopupHidden: function PM__onPopupHidden(aEvent) { - // Avoid handling popuphidden of inner views. - let popup = aEvent.originalTarget; - let placesNode = popup._placesNode; - if (!placesNode || PlacesUIUtils.getViewForNode(popup) != this) - return; - - // UI performance: folder queries are cheap, keep the resultnode open - // so we don't rebuild its contents whenever the popup is reopened. - // Though, we want to always close feed containers so their expiration - // status will be checked at next opening. - if (!PlacesUtils.nodeIsFolder(placesNode) || - this.controller.hasCachedLivemarkInfo(placesNode)) - placesNode.containerOpen = false; - - // The autoopened attribute is set for folders which have been - // automatically opened when dragged over. Turn off this attribute - // when the folder closes because it is no longer applicable. - popup.removeAttribute("autoopened"); - popup.removeAttribute("dragstart"); - } -}; - diff --git a/components/places/content/controller.js b/components/places/content/controller.js deleted file mode 100644 index f4e272e..0000000 --- a/components/places/content/controller.js +++ /dev/null @@ -1,1895 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); -Components.utils.import("resource://gre/modules/ForgetAboutSite.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", - "resource://gre/modules/NetUtil.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", - "resource://gre/modules/PrivateBrowsingUtils.jsm"); - -// XXXmano: we should move most/all of these constants to PlacesUtils -const ORGANIZER_ROOT_BOOKMARKS = "place:folder=BOOKMARKS_MENU&excludeItems=1&queryType=1"; - -// No change to the view, preserve current selection -const RELOAD_ACTION_NOTHING = 0; -// Inserting items new to the view, select the inserted rows -const RELOAD_ACTION_INSERT = 1; -// Removing items from the view, select the first item after the last selected -const RELOAD_ACTION_REMOVE = 2; -// Moving items within a view, don't treat the dropped items as additional -// rows. -const RELOAD_ACTION_MOVE = 3; - -// When removing a bunch of pages we split them in chunks to give some breath -// to the main-thread. -const REMOVE_PAGES_CHUNKLEN = 300; - -/** - * Represents an insertion point within a container where we can insert - * items. - * @param aItemId - * The identifier of the parent container - * @param aIndex - * The index within the container where we should insert - * @param aOrientation - * The orientation of the insertion. NOTE: the adjustments to the - * insertion point to accommodate the orientation should be done by - * the person who constructs the IP, not the user. The orientation - * is provided for informational purposes only! - * @param [optional] aIsTag - * Indicates if parent container is a tag - * @param [optional] aDropNearItemId - * When defined we will calculate index based on this itemId - * @constructor - */ -function InsertionPoint(aItemId, aIndex, aOrientation, aIsTag, - aDropNearItemId) { - this.itemId = aItemId; - this._index = aIndex; - this.orientation = aOrientation; - this.isTag = aIsTag; - this.dropNearItemId = aDropNearItemId; -} - -InsertionPoint.prototype = { - set index(val) { - return this._index = val; - }, - - get index() { - if (this.dropNearItemId > 0) { - // If dropNearItemId is set up we must calculate the real index of - // the item near which we will drop. - var index = PlacesUtils.bookmarks.getItemIndex(this.dropNearItemId); - return this.orientation == Ci.nsITreeView.DROP_BEFORE ? index : index + 1; - } - return this._index; - } -}; - -/** - * Places Controller - */ - -function PlacesController(aView) { - this._view = aView; - XPCOMUtils.defineLazyServiceGetter(this, "clipboard", - "@mozilla.org/widget/clipboard;1", - "nsIClipboard"); - XPCOMUtils.defineLazyGetter(this, "profileName", function () { - return Services.dirsvc.get("ProfD", Ci.nsIFile).leafName; - }); - - this._cachedLivemarkInfoObjects = new Map(); -} - -PlacesController.prototype = { - /** - * The places view. - */ - _view: null, - - QueryInterface: XPCOMUtils.generateQI([ - Ci.nsIClipboardOwner - ]), - - // nsIClipboardOwner - LosingOwnership: function PC_LosingOwnership (aXferable) { - this.cutNodes = []; - }, - - terminate: function PC_terminate() { - this._releaseClipboardOwnership(); - }, - - supportsCommand: function PC_supportsCommand(aCommand) { - // Non-Places specific commands that we also support - switch (aCommand) { - case "cmd_undo": - case "cmd_redo": - case "cmd_cut": - case "cmd_copy": - case "cmd_paste": - case "cmd_delete": - case "cmd_selectAll": - return true; - } - - // All other Places Commands are prefixed with "placesCmd_" ... this - // filters out other commands that we do _not_ support (see 329587). - const CMD_PREFIX = "placesCmd_"; - return (aCommand.substr(0, CMD_PREFIX.length) == CMD_PREFIX); - }, - - isCommandEnabled: function PC_isCommandEnabled(aCommand) { - switch (aCommand) { - case "cmd_undo": - return PlacesUtils.transactionManager.numberOfUndoItems > 0; - case "cmd_redo": - return PlacesUtils.transactionManager.numberOfRedoItems > 0; - case "cmd_cut": - case "placesCmd_cut": - case "placesCmd_moveBookmarks": - for (let node of this._view.selectedNodes) { - // If selection includes history nodes or tags-as-bookmark, disallow - // cutting. - if (node.itemId == -1 || - (node.parent && PlacesUtils.nodeIsTagQuery(node.parent))) { - return false; - } - } - // Otherwise fall through to the cmd_delete check. - case "cmd_delete": - case "placesCmd_delete": - case "placesCmd_deleteDataHost": - return this._hasRemovableSelection(); - case "cmd_copy": - case "placesCmd_copy": - return this._view.hasSelection; - case "cmd_paste": - case "placesCmd_paste": - return this._canInsert(true) && this._isClipboardDataPasteable(); - case "cmd_selectAll": - if (this._view.selType != "single") { - let rootNode = this._view.result.root; - if (rootNode.containerOpen && rootNode.childCount > 0) - return true; - } - return false; - case "placesCmd_open": - case "placesCmd_open:window": - case "placesCmd_open:privatewindow": - case "placesCmd_open:tab": - var selectedNode = this._view.selectedNode; - return selectedNode && PlacesUtils.nodeIsURI(selectedNode); - case "placesCmd_new:folder": - case "placesCmd_new:livemark": - return this._canInsert(); - case "placesCmd_new:bookmark": - return this._canInsert(); - case "placesCmd_new:separator": - return this._canInsert() && - !PlacesUtils.asQuery(this._view.result.root).queryOptions.excludeItems && - this._view.result.sortingMode == - Ci.nsINavHistoryQueryOptions.SORT_BY_NONE; - case "placesCmd_show:info": - var selectedNode = this._view.selectedNode; - return selectedNode && PlacesUtils.getConcreteItemId(selectedNode) != -1 - case "placesCmd_reload": - // Livemark containers - var selectedNode = this._view.selectedNode; - return selectedNode && this.hasCachedLivemarkInfo(selectedNode); - case "placesCmd_sortBy:name": - var selectedNode = this._view.selectedNode; - return selectedNode && - PlacesUtils.nodeIsFolder(selectedNode) && - !PlacesUIUtils.isContentsReadOnly(selectedNode) && - this._view.result.sortingMode == - Ci.nsINavHistoryQueryOptions.SORT_BY_NONE; - case "placesCmd_createBookmark": - var node = this._view.selectedNode; - return node && PlacesUtils.nodeIsURI(node) && node.itemId == -1; - case "placesCmd_openParentFolder": - return true; - default: - return false; - } - }, - - doCommand: function PC_doCommand(aCommand) { - switch (aCommand) { - case "cmd_undo": - PlacesUtils.transactionManager.undoTransaction(); - break; - case "cmd_redo": - PlacesUtils.transactionManager.redoTransaction(); - break; - case "cmd_cut": - case "placesCmd_cut": - this.cut(); - break; - case "cmd_copy": - case "placesCmd_copy": - this.copy(); - break; - case "cmd_paste": - case "placesCmd_paste": - this.paste(); - break; - case "cmd_delete": - case "placesCmd_delete": - this.remove("Remove Selection"); - break; - case "placesCmd_deleteDataHost": - var host; - if (PlacesUtils.nodeIsHost(this._view.selectedNode)) { - var queries = this._view.selectedNode.getQueries(); - host = queries[0].domain; - } - else - host = NetUtil.newURI(this._view.selectedNode.uri).host; - ForgetAboutSite.removeDataFromDomain(host) - .catch(Components.utils.reportError); - break; - case "cmd_selectAll": - this.selectAll(); - break; - case "placesCmd_open": - PlacesUIUtils.openNodeIn(this._view.selectedNode, "current", this._view); - break; - case "placesCmd_open:window": - PlacesUIUtils.openNodeIn(this._view.selectedNode, "window", this._view); - break; - case "placesCmd_open:privatewindow": - PlacesUIUtils.openNodeIn(this._view.selectedNode, "window", this._view, true); - break; - case "placesCmd_open:tab": - PlacesUIUtils.openNodeIn(this._view.selectedNode, "tab", this._view); - break; - case "placesCmd_new:folder": - this.newItem("folder"); - break; - case "placesCmd_new:bookmark": - this.newItem("bookmark"); - break; - case "placesCmd_new:livemark": - this.newItem("livemark"); - break; - case "placesCmd_new:separator": - this.newSeparator(); - break; - case "placesCmd_show:info": - this.showBookmarkPropertiesForSelection(); - break; - case "placesCmd_moveBookmarks": - this.moveSelectedBookmarks(); - break; - case "placesCmd_reload": - this.reloadSelectedLivemark(); - break; - case "placesCmd_sortBy:name": - this.sortFolderByName(); - break; - case "placesCmd_createBookmark": - let node = this._view.selectedNode; - PlacesUIUtils.showBookmarkDialog({ action: "add" - , type: "bookmark" - , hiddenRows: [ "description" - , "keyword" - , "location" - , "loadInSidebar" ] - , uri: NetUtil.newURI(node.uri) - , title: node.title - }, window.top); - break; - case "placesCmd_openParentFolder": - this.openParentFolder(); - break; - } - }, - - onEvent: function PC_onEvent(eventName) { }, - - - /** - * Determine whether or not the selection can be removed, either by the - * delete or cut operations based on whether or not any of its contents - * are non-removable. We don't need to worry about recursion here since it - * is a policy decision that a removable item not be placed inside a non- - * removable item. - * @returns true if all nodes in the selection can be removed, - * false otherwise. - */ - _hasRemovableSelection() { - var ranges = this._view.removableSelectionRanges; - if (!ranges.length) - return false; - - var root = this._view.result.root; - - for (var j = 0; j < ranges.length; j++) { - var nodes = ranges[j]; - for (var i = 0; i < nodes.length; ++i) { - // Disallow removing the view's root node - if (nodes[i] == root) - return false; - - if (!PlacesUIUtils.canUserRemove(nodes[i])) - return false; - } - } - - return true; - }, - - /** - * Determines whether or not nodes can be inserted relative to the selection. - */ - _canInsert: function PC__canInsert(isPaste) { - var ip = this._view.insertionPoint; - return ip != null && (isPaste || ip.isTag != true); - }, - - /** - * Looks at the data on the clipboard to see if it is paste-able. - * Paste-able data is: - * - in a format that the view can receive - * @returns true if: - clipboard data is of a TYPE_X_MOZ_PLACE_* flavor, - - clipboard data is of type TEXT_UNICODE and - is a valid URI. - */ - _isClipboardDataPasteable: function PC__isClipboardDataPasteable() { - // if the clipboard contains TYPE_X_MOZ_PLACE_* data, it is definitely - // pasteable, with no need to unwrap all the nodes. - - var flavors = PlacesControllerDragHelper.placesFlavors; - var clipboard = this.clipboard; - var hasPlacesData = - clipboard.hasDataMatchingFlavors(flavors, flavors.length, - Ci.nsIClipboard.kGlobalClipboard); - if (hasPlacesData) - return this._view.insertionPoint != null; - - // if the clipboard doesn't have TYPE_X_MOZ_PLACE_* data, we also allow - // pasting of valid "text/unicode" and "text/x-moz-url" data - var xferable = Cc["@mozilla.org/widget/transferable;1"]. - createInstance(Ci.nsITransferable); - xferable.init(null); - - xferable.addDataFlavor(PlacesUtils.TYPE_X_MOZ_URL); - xferable.addDataFlavor(PlacesUtils.TYPE_UNICODE); - clipboard.getData(xferable, Ci.nsIClipboard.kGlobalClipboard); - - try { - // getAnyTransferData will throw if no data is available. - var data = { }, type = { }; - xferable.getAnyTransferData(type, data, { }); - data = data.value.QueryInterface(Ci.nsISupportsString).data; - if (type.value != PlacesUtils.TYPE_X_MOZ_URL && - type.value != PlacesUtils.TYPE_UNICODE) - return false; - - // unwrapNodes() will throw if the data blob is malformed. - var unwrappedNodes = PlacesUtils.unwrapNodes(data, type.value); - return this._view.insertionPoint != null; - } - catch (e) { - // getAnyTransferData or unwrapNodes failed - return false; - } - }, - - /** - * Gathers information about the selected nodes according to the following - * rules: - * "link" node is a URI - * "bookmark" node is a bookmark - * "livemarkChild" node is a child of a livemark - * "tagChild" node is a child of a tag - * "folder" node is a folder - * "query" node is a query - * "separator" node is a separator line - * "host" node is a host - * - * @returns an array of objects corresponding the selected nodes. Each - * object has each of the properties above set if its corresponding - * node matches the rule. In addition, the annotations names for each - * node are set on its corresponding object as properties. - * Notes: - * 1) This can be slow, so don't call it anywhere performance critical! - */ - _buildSelectionMetadata: function PC__buildSelectionMetadata() { - var metadata = []; - var nodes = this._view.selectedNodes; - - for (var i = 0; i < nodes.length; i++) { - var nodeData = {}; - var node = nodes[i]; - var nodeType = node.type; - var uri = null; - - // We don't use the nodeIs* methods here to avoid going through the type - // property way too often - switch (nodeType) { - case Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY: - nodeData["query"] = true; - if (node.parent) { - switch (PlacesUtils.asQuery(node.parent).queryOptions.resultType) { - case Ci.nsINavHistoryQueryOptions.RESULTS_AS_SITE_QUERY: - nodeData["host"] = true; - break; - case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_SITE_QUERY: - case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY: - nodeData["day"] = true; - break; - } - } - break; - case Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER: - case Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT: - nodeData["folder"] = true; - break; - case Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR: - nodeData["separator"] = true; - break; - case Ci.nsINavHistoryResultNode.RESULT_TYPE_URI: - nodeData["link"] = true; - uri = NetUtil.newURI(node.uri); - if (PlacesUtils.nodeIsBookmark(node)) { - nodeData["bookmark"] = true; - var parentNode = node.parent; - if (parentNode) { - if (PlacesUtils.nodeIsTagQuery(parentNode)) - nodeData["tagChild"] = true; - } - } else { - var parentNode = node.parent; - if (parentNode) { - if (this.hasCachedLivemarkInfo(parentNode)) - nodeData["livemarkChild"] = true; - } - } - break; - } - - // annotations - if (uri) { - let names = PlacesUtils.annotations.getPageAnnotationNames(uri); - for (let j = 0; j < names.length; ++j) - nodeData[names[j]] = true; - } - - // For items also include the item-specific annotations - if (node.itemId != -1) { - let names = PlacesUtils.annotations - .getItemAnnotationNames(node.itemId); - for (let j = 0; j < names.length; ++j) - nodeData[names[j]] = true; - } - metadata.push(nodeData); - } - - return metadata; - }, - - /** - * Determines if a context-menu item should be shown - * @param aMenuItem - * the context menu item - * @param aMetaData - * meta data about the selection - * @returns true if the conditions (see buildContextMenu) are satisfied - * and the item can be displayed, false otherwise. - */ - _shouldShowMenuItem: function PC__shouldShowMenuItem(aMenuItem, aMetaData) { - var selectiontype = aMenuItem.getAttribute("selectiontype"); - if (!selectiontype) { - selectiontype = "single|multiple"; - } - var selectionTypes = selectiontype.split("|"); - if (selectionTypes.indexOf("any") != -1) { - return true; - } - var count = aMetaData.length; - if (count > 1 && selectionTypes.indexOf("multiple") == -1) - return false; - if (count == 1 && selectionTypes.indexOf("single") == -1) - return false; - // NB: if there is no selection, we show the item if (and only if) - // the selectiontype includes 'none' - the metadata list will be - // empty so none of the other criteria will apply anyway. - if (count == 0) - return selectionTypes.indexOf("none") != -1; - - var forceHideAttr = aMenuItem.getAttribute("forcehideselection"); - if (forceHideAttr) { - var forceHideRules = forceHideAttr.split("|"); - for (let i = 0; i < aMetaData.length; ++i) { - for (let j = 0; j < forceHideRules.length; ++j) { - if (forceHideRules[j] in aMetaData[i]) - return false; - } - } - } - - var selectionAttr = aMenuItem.getAttribute("selection"); - if (!selectionAttr) { - return !aMenuItem.hidden; - } - - if (selectionAttr == "any") - return true; - - var showRules = selectionAttr.split("|"); - var anyMatched = false; - function metaDataNodeMatches(metaDataNode, rules) { - for (var i = 0; i < rules.length; i++) { - if (rules[i] in metaDataNode) - return true; - } - return false; - } - - for (var i = 0; i < aMetaData.length; ++i) { - if (metaDataNodeMatches(aMetaData[i], showRules)) - anyMatched = true; - else - return false; - } - return anyMatched; - }, - - /** - * Detects information (meta-data rules) about the current selection in the - * view (see _buildSelectionMetadata) and sets the visibility state for each - * of the menu-items in the given popup with the following rules applied: - * 1) The "selectiontype" attribute may be set on a menu-item to "single" - * if the menu-item should be visible only if there is a single node - * selected, or to "multiple" if the menu-item should be visible only if - * multiple nodes are selected, or to "none" if the menuitems should be - * visible for if there are no selected nodes, or to a |-separated - * combination of these. - * If the attribute is not set or set to an invalid value, the menu-item - * may be visible irrespective of the selection. - * 2) The "selection" attribute may be set on a menu-item to the various - * meta-data rules for which it may be visible. The rules should be - * separated with the | character. - * 3) A menu-item may be visible only if at least one of the rules set in - * its selection attribute apply to each of the selected nodes in the - * view. - * 4) The "forcehideselection" attribute may be set on a menu-item to rules - * for which it should be hidden. This attribute takes priority over the - * selection attribute. A menu-item would be hidden if at least one of the - * given rules apply to one of the selected nodes. The rules should be - * separated with the | character. - * 5) The "hideifnoinsertionpoint" attribute may be set on a menu-item to - * true if it should be hidden when there's no insertion point - * 6) The visibility state of a menu-item is unchanged if none of these - * attribute are set. - * 7) These attributes should not be set on separators for which the - * visibility state is "auto-detected." - * 8) The "hideifprivatebrowsing" attribute may be set on a menu-item to - * true if it should be hidden inside the private browsing mode - * @param aPopup - * The menupopup to build children into. - * @return true if at least one item is visible, false otherwise. - */ - buildContextMenu: function PC_buildContextMenu(aPopup) { - var metadata = this._buildSelectionMetadata(); - var ip = this._view.insertionPoint; - var noIp = !ip || ip.isTag; - - var separator = null; - var visibleItemsBeforeSep = false; - var usableItemCount = 0; - for (var i = 0; i < aPopup.childNodes.length; ++i) { - var item = aPopup.childNodes[i]; - if (item.localName != "menuseparator") { - // We allow pasting into tag containers, so special case that. - var hideIfNoIP = item.getAttribute("hideifnoinsertionpoint") == "true" && - noIp && !(ip && ip.isTag && item.id == "placesContext_paste"); - // Show the "Open Containing Folder" menu-item only when the context is - // in the Library or in the Sidebar, and only when there's no insertion - // point. - var hideParentFolderItem = item.id == "placesContext_openParentFolder" && - (!/tree/i.test(this._view.localName) || ip); - var hideIfPrivate = item.getAttribute("hideifprivatebrowsing") == "true" && - PrivateBrowsingUtils.isWindowPrivate(window); - var shouldHideItem = hideIfNoIP || hideIfPrivate || hideParentFolderItem || - !this._shouldShowMenuItem(item, metadata); - item.hidden = item.disabled = shouldHideItem; - - if (!item.hidden) { - visibleItemsBeforeSep = true; - usableItemCount++; - - // Show the separator above the menu-item if any - if (separator) { - separator.hidden = false; - separator = null; - } - } - } - else { // menuseparator - // Initially hide it. It will be unhidden if there will be at least one - // visible menu-item above and below it. - item.hidden = true; - - // We won't show the separator at all if no items are visible above it - if (visibleItemsBeforeSep) - separator = item; - - // New separator, count again: - visibleItemsBeforeSep = false; - } - } - - // Set Open Folder/Links In Tabs items enabled state if they're visible - if (usableItemCount > 0) { - var openContainerInTabsItem = document.getElementById("placesContext_openContainer:tabs"); - if (!openContainerInTabsItem.hidden) { - var containerToUse = this._view.selectedNode || this._view.result.root; - if (PlacesUtils.nodeIsContainer(containerToUse)) { - if (!PlacesUtils.hasChildURIs(containerToUse, true)) { - openContainerInTabsItem.disabled = true; - // Ensure that we don't display the menu if nothing is enabled: - usableItemCount--; - } - } - } - } - - return usableItemCount > 0; - }, - - /** - * Select all links in the current view. - */ - selectAll: function PC_selectAll() { - this._view.selectAll(); - }, - - /** - * Opens the bookmark properties for the selected URI Node. - */ - showBookmarkPropertiesForSelection: - function PC_showBookmarkPropertiesForSelection() { - var node = this._view.selectedNode; - if (!node) - return; - - var itemType = PlacesUtils.nodeIsFolder(node) || - PlacesUtils.nodeIsTagQuery(node) ? "folder" : "bookmark"; - var concreteId = PlacesUtils.getConcreteItemId(node); - var isRootItem = PlacesUtils.isRootItem(concreteId); - var itemId = node.itemId; - if (isRootItem || PlacesUtils.nodeIsTagQuery(node)) { - // If this is a root or the Tags query we use the concrete itemId to catch - // the correct title for the node. - itemId = concreteId; - } - - PlacesUIUtils.showBookmarkDialog({ action: "edit" - , type: itemType - , itemId: itemId - , readOnly: isRootItem - , hiddenRows: [ "folderPicker" ] - }, window.top); - }, - - /** - * This method can be run on a URI parameter to ensure that it didn't - * receive a string instead of an nsIURI object. - */ - _assertURINotString: function PC__assertURINotString(value) { - NS_ASSERT((typeof(value) == "object") && !(value instanceof String), - "This method should be passed a URI as a nsIURI object, not as a string."); - }, - - /** - * Reloads the selected livemark if any. - */ - reloadSelectedLivemark: function PC_reloadSelectedLivemark() { - var selectedNode = this._view.selectedNode; - if (selectedNode) { - let itemId = selectedNode.itemId; - PlacesUtils.livemarks.getLivemark({ id: itemId }) - .then(aLivemark => { - aLivemark.reload(true); - }, Components.utils.reportError); - } - }, - - /** - * Opens the links in the selected folder, or the selected links in new tabs. - */ - openSelectionInTabs: function PC_openLinksInTabs(aEvent) { - var node = this._view.selectedNode; - var nodes = this._view.selectedNodes; - // In the case of no selection, open the root node: - if (!node && !nodes.length) { - node = this._view.result.root; - } - if (node && PlacesUtils.nodeIsContainer(node)) - PlacesUIUtils.openContainerNodeInTabs(node, aEvent, this._view); - else - PlacesUIUtils.openURINodesInTabs(nodes, aEvent, this._view); - }, - - /** - * Shows the Add Bookmark UI for the current insertion point. - * - * @param aType - * the type of the new item (bookmark/livemark/folder) - */ - newItem: function PC_newItem(aType) { - let ip = this._view.insertionPoint; - if (!ip) - throw Cr.NS_ERROR_NOT_AVAILABLE; - - let performed = - PlacesUIUtils.showBookmarkDialog({ action: "add" - , type: aType - , defaultInsertionPoint: ip - , hiddenRows: [ "folderPicker" ] - }, window.top); - if (performed) { - // Select the new item. - let insertedNodeId = PlacesUtils.bookmarks - .getIdForItemAt(ip.itemId, ip.index); - this._view.selectItems([insertedNodeId], false); - } - }, - - /** - * Create a new Bookmark separator somewhere. - */ - newSeparator: function PC_newSeparator() { - var ip = this._view.insertionPoint; - if (!ip) - throw Cr.NS_ERROR_NOT_AVAILABLE; - var txn = new PlacesCreateSeparatorTransaction(ip.itemId, ip.index); - PlacesUtils.transactionManager.doTransaction(txn); - // select the new item - var insertedNodeId = PlacesUtils.bookmarks - .getIdForItemAt(ip.itemId, ip.index); - this._view.selectItems([insertedNodeId], false); - }, - - /** - * Opens a dialog for moving the selected nodes. - */ - moveSelectedBookmarks: function PC_moveBookmarks() { - window.openDialog("chrome://browser/content/places/moveBookmarks.xul", - "", "chrome, modal", - this._view.selectedNodes); - }, - - /** - * Sort the selected folder by name. - */ - sortFolderByName: function PC_sortFolderByName() { - var itemId = PlacesUtils.getConcreteItemId(this._view.selectedNode); - var txn = new PlacesSortFolderByNameTransaction(itemId); - PlacesUtils.transactionManager.doTransaction(txn); - }, - - /** - * Open the parent folder for the selected bookmarks search result. - */ - openParentFolder: function PC_openParentFolder() { - var view; - if (!document.popupNode) { - view = document.commandDispatcher.focusedElement; - } else { - view = PlacesUIUtils.getViewForNode(document.popupNode); // XULElement - } - if (!view || view.getAttribute("type") != "places") - return; - var node = view.selectedNode; // nsINavHistoryResultNode - var aItemId = node.itemId; - var aFolderItemId = this.getParentFolderByItemId(aItemId); - if (aFolderItemId) - this.selectFolderByItemId(view, aFolderItemId, aItemId); - }, - - getParentFolderByItemId: function PC_getParentFolderByItemId(aItemId) { - var bmsvc = Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"]. - getService(Components.interfaces.nsINavBookmarksService); - var parentFolderId = bmsvc.getFolderIdForItem(aItemId); - - return parentFolderId; - }, - - selectItems2: function PC_selectItems2(view, aIDs) { - var ids = aIDs; // Don't manipulate the caller's array. - - // Array of nodes found by findNodes which are to be selected - var nodes = []; - - // Array of nodes found by findNodes which should be opened - var nodesToOpen = []; - - // A set of URIs of container-nodes that were previously searched, - // and thus shouldn't be searched again. This is empty at the initial - // start of the recursion and gets filled in as the recursion - // progresses. - var nodesURIChecked = []; - - /** - * Recursively search through a node's children for items - * with the given IDs. When a matching item is found, remove its ID - * from the IDs array, and add the found node to the nodes dictionary. - * - * NOTE: This method will leave open any node that had matching items - * in its subtree. - */ - function findNodes(node) { - var foundOne = false; - // See if node matches an ID we wanted; add to results. - // For simple folder queries, check both itemId and the concrete - // item id. - var index = ids.indexOf(node.itemId); - if (index == -1 && - node.type == Components.interfaces.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT) { - index = ids.indexOf(PlacesUtils.asQuery(node).folderItemId); //xxx Bug 556739 3.7a5pre - } - - if (index != -1) { - nodes.push(node); - foundOne = true; - ids.splice(index, 1); - } - - if (ids.length == 0 || !PlacesUtils.nodeIsContainer(node) || - nodesURIChecked.indexOf(node.uri) != -1) - return foundOne; - - nodesURIChecked.push(node.uri); - PlacesUtils.asContainer(node); // xxx Bug 556739 3.7a6pre - - // Remember the beginning state so that we can re-close - // this node if we don't find any additional results here. - var previousOpenness = node.containerOpen; - node.containerOpen = true; - for (var child = 0; child < node.childCount && ids.length > 0; - child++) { - var childNode = node.getChild(child); - var found = findNodes(childNode); - if (!foundOne) - foundOne = found; - } - - // If we didn't find any additional matches in this node's - // subtree, revert the node to its previous openness. - if (foundOne) - nodesToOpen.unshift(node); - node.containerOpen = previousOpenness; - return foundOne; - } // findNodes - - // Disable notifications while looking for nodes. - let result = view.result; - let didSuppressNotifications = result.suppressNotifications; - if (!didSuppressNotifications) - result.suppressNotifications = true - try { - findNodes(view.result.root); - } - finally { - if (!didSuppressNotifications) - result.suppressNotifications = false; - } - - // For all the nodes we've found, highlight the corresponding - // index in the tree. - var resultview = view.view; - var selection = resultview.selection; - selection.selectEventsSuppressed = true; - selection.clearSelection(); - // Open nodes containing found items. - for (var i = 0; i < nodesToOpen.length; i++) { - nodesToOpen[i].containerOpen = true; - } - for (var i = 0; i < nodes.length; i++) { - if (PlacesUtils.nodeIsContainer(nodes[i])) - continue; - - var index = resultview.treeIndexForNode(nodes[i]); - selection.rangedSelect(index, index, true); - } - selection.selectEventsSuppressed = false; - }, - - selectFolderByItemId: function PC_selectFolderByItemId(view, aFolderItemId, aItemId) { - // Library - if (view.getAttribute("id") == "placeContent") { - view = document.getElementById("placesList"); - // Select a folder node in folder pane. - this.selectItems2(view, [aFolderItemId]); - view.selectItems([aFolderItemId]); - if (view.currentIndex) - view.treeBoxObject.ensureRowIsVisible(view.currentIndex); - // Reselect child node. - setTimeout(function(aItemId, view) { - var aView = view.ownerDocument.getElementById("placeContent"); - aView.selectItems([aItemId]); - if (aView.currentIndex) - aView.treeBoxObject.ensureRowIsVisible(aView.currentIndex); - }, 0, aItemId, view); - return; - } - - // Bookmarks Sidebar - if (!view) - return; - view.place = view.place; - - if ('FlatBookmarksOverlay' in window) { - var sidebarwin = view.ownerDocument.defaultView; - var searchBox = sidebarwin.document.getElementById("search-box"); - searchBox.value = ""; - searchBox.doCommand(); - sidebarwin.FlatBookmarks._setTreePlace(sidebarwin.FlatBookmarks._makePlaceForFolder(aFolderItemId)); - view.selectItems([aItemId]); - var tbo = view.treeBoxObject; - tbo.ensureRowIsVisible(view.currentIndex); - view.focus(); - return; - } - - view.findNode = function flatChildNodes(node, aIDs) { - var ids = aIDs; // Don't manipulate the caller's array. - - // Array of nodes found by findNodes which are to be selected - var nodes = []; - - // Array of nodes found by findNodes which should be opened - var nodesToOpen = []; - - // A set of URIs of container-nodes that were previously searched, - // and thus shouldn't be searched again. This is empty at the initial - // start of the recursion and gets filled in as the recursion - // progresses. - var nodesURIChecked = []; - - /** - * Recursively search through a node's children for items - * with the given IDs. When a matching item is found, remove its ID - * from the IDs array, and add the found node to the nodes dictionary. - * - * NOTE: This method will leave open any node that had matching items - * in its subtree. - */ - function findNodes(node) { - var foundOne = false; - // See if node matches an ID we wanted; add to results. - // For simple folder queries, check both itemId and the concrete - // item id. - var index = ids.indexOf(node.itemId); - if (index == -1 && - node.type == Components.interfaces.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT) { - index = ids.indexOf(PlacesUtils.asQuery(node).folderItemId); // xxx Bug 556739 3.7a5pre - } - - if (index != -1) { - nodes.push(node); - foundOne = true; - ids.splice(index, 1); - } - - if (ids.length == 0 || !PlacesUtils.nodeIsContainer(node) || - nodesURIChecked.indexOf(node.uri) != -1) - return foundOne; - - nodesURIChecked.push(node.uri); - PlacesUtils.asContainer(node); // xxx Bug 556739 3.7a6pre - // Remember the beginning state so that we can re-close - // this node if we don't find any additional results here. - var previousOpenness = node.containerOpen; - node.containerOpen = true; - for (var child = 0; child < node.childCount && ids.length > 0; - child++) { - var childNode = node.getChild(child); - if (PlacesUtils.nodeIsQuery(childNode)) - continue; - var found = findNodes(childNode); - if (!foundOne) - foundOne = found; - } - - // If we didn't find any additional matches in this node's - // subtree, revert the node to its previous openness. - if (foundOne) - nodesToOpen.unshift(node); - node.containerOpen = previousOpenness; - return foundOne; - } // findNodes - - // Disable notifications while looking for nodes. - let result = this.result; - let didSuppressNotifications = result.suppressNotifications; - if (!didSuppressNotifications) - result.suppressNotifications = true - try { - findNodes(this.result.root); - } - finally { - if (!didSuppressNotifications) - result.suppressNotifications = false; - } - - // Open nodes containing found items. - for (var i = 0; i < nodesToOpen.length; i++) { - nodesToOpen[i].containerOpen = true; - } - return nodes; - }; // findNode - - // For all the nodes we've found, highlight the corresponding - // index in the tree. - var resultview = view.view; - var selection = view.view.selection; - selection.selectEventsSuppressed = true; - selection.clearSelection(); - var nodes = view.findNode(view.result.root, [aFolderItemId]); - if (nodes.length > 0) { - var index = resultview.treeIndexForNode(nodes[0]); - nodes = view.findNode(nodes[0], [aItemId]); - if (nodes.length > 0) { - index = resultview.treeIndexForNode(nodes[0]); - selection.rangedSelect(index, index, true); - } - } - selection.selectEventsSuppressed = false; - - var tbo = view.treeBoxObject; - tbo.ensureRowIsVisible(view.currentIndex); - view.focus(); - return; - }, - - /** - * Walk the list of folders we're removing in this delete operation, and - * see if the selected node specified is already implicitly being removed - * because it is a child of that folder. - * @param node - * Node to check for containment. - * @param pastFolders - * List of folders the calling function has already traversed - * @returns true if the node should be skipped, false otherwise. - */ - _shouldSkipNode: function PC_shouldSkipNode(node, pastFolders) { - /** - * Determines if a node is contained by another node within a resultset. - * @param node - * The node to check for containment for - * @param parent - * The parent container to check for containment in - * @returns true if node is a member of parent's children, false otherwise. - */ - function isContainedBy(node, parent) { - var cursor = node.parent; - while (cursor) { - if (cursor == parent) - return true; - cursor = cursor.parent; - } - return false; - } - - for (var j = 0; j < pastFolders.length; ++j) { - if (isContainedBy(node, pastFolders[j])) - return true; - } - return false; - }, - - /** - * Creates a set of transactions for the removal of a range of items. - * A range is an array of adjacent nodes in a view. - * @param [in] range - * An array of nodes to remove. Should all be adjacent. - * @param [out] transactions - * An array of transactions. - * @param [optional] removedFolders - * An array of folder nodes that have already been removed. - */ - _removeRange: function PC__removeRange(range, transactions, removedFolders) { - NS_ASSERT(transactions instanceof Array, "Must pass a transactions array"); - if (!removedFolders) - removedFolders = []; - - for (var i = 0; i < range.length; ++i) { - var node = range[i]; - if (this._shouldSkipNode(node, removedFolders)) - continue; - - if (PlacesUtils.nodeIsTagQuery(node.parent)) { - // This is a uri node inside a tag container. It needs a special - // untag transaction. - var tagItemId = PlacesUtils.getConcreteItemId(node.parent); - var uri = NetUtil.newURI(node.uri); - let txn = new PlacesUntagURITransaction(uri, [tagItemId]); - transactions.push(txn); - } - else if (PlacesUtils.nodeIsTagQuery(node) && node.parent && - PlacesUtils.nodeIsQuery(node.parent) && - PlacesUtils.asQuery(node.parent).queryOptions.resultType == - Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY) { - // This is a tag container. - // Untag all URIs tagged with this tag only if the tag container is - // child of the "Tags" query in the library, in all other places we - // must only remove the query node. - var tag = node.title; - var URIs = PlacesUtils.tagging.getURIsForTag(tag); - for (var j = 0; j < URIs.length; j++) { - let txn = new PlacesUntagURITransaction(URIs[j], [tag]); - transactions.push(txn); - } - } - else if (PlacesUtils.nodeIsURI(node) && - PlacesUtils.nodeIsQuery(node.parent) && - PlacesUtils.asQuery(node.parent).queryOptions.queryType == - Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) { - // This is a uri node inside an history query. - PlacesUtils.bhistory.removePage(NetUtil.newURI(node.uri)); - // History deletes are not undoable, so we don't have a transaction. - } - else if (node.itemId == -1 && - PlacesUtils.nodeIsQuery(node) && - PlacesUtils.asQuery(node).queryOptions.queryType == - Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) { - // This is a dynamically generated history query, like queries - // grouped by site, time or both. Dynamically generated queries don't - // have an itemId even if they are descendants of a bookmark. - this._removeHistoryContainer(node); - // History deletes are not undoable, so we don't have a transaction. - } - else { - // This is a common bookmark item. - if (PlacesUtils.nodeIsFolder(node)) { - // If this is a folder we add it to our array of folders, used - // to skip nodes that are children of an already removed folder. - removedFolders.push(node); - } - let txn = new PlacesRemoveItemTransaction(node.itemId); - transactions.push(txn); - } - } - }, - - /** - * Removes the set of selected ranges from bookmarks. - * @param txnName - * See |remove|. - */ - _removeRowsFromBookmarks: function PC__removeRowsFromBookmarks(txnName) { - var ranges = this._view.removableSelectionRanges; - var transactions = []; - var removedFolders = []; - - for (var i = 0; i < ranges.length; i++) - this._removeRange(ranges[i], transactions, removedFolders); - - if (transactions.length > 0) { - var txn = new PlacesAggregatedTransaction(txnName, transactions); - PlacesUtils.transactionManager.doTransaction(txn); - } - }, - - /** - * Removes the set of selected ranges from history. - * - * @note history deletes are not undoable. - */ - _removeRowsFromHistory: function PC__removeRowsFromHistory() { - let nodes = this._view.selectedNodes; - let URIs = []; - for (let i = 0; i < nodes.length; ++i) { - let node = nodes[i]; - if (PlacesUtils.nodeIsURI(node)) { - let uri = NetUtil.newURI(node.uri); - // Avoid duplicates. - if (URIs.indexOf(uri) < 0) { - URIs.push(uri); - } - } - else if (PlacesUtils.nodeIsQuery(node) && - PlacesUtils.asQuery(node).queryOptions.queryType == - Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) { - this._removeHistoryContainer(node); - } - } - - // Do removal in chunks to give some breath to main-thread. - function pagesChunkGenerator(aURIs) { - while (aURIs.length) { - let URIslice = aURIs.splice(0, REMOVE_PAGES_CHUNKLEN); - PlacesUtils.bhistory.removePages(URIslice, URIslice.length); - Services.tm.mainThread.dispatch(function() { - try { - gen.next(); - } catch (ex if ex instanceof StopIteration) {} - }, Ci.nsIThread.DISPATCH_NORMAL); - yield; - } - } - let gen = pagesChunkGenerator(URIs); - gen.next(); - }, - - /** - * Removes history visits for an history container node. - * @param [in] aContainerNode - * The container node to remove. - * - * @note history deletes are not undoable. - */ - _removeHistoryContainer: function PC__removeHistoryContainer(aContainerNode) { - if (PlacesUtils.nodeIsHost(aContainerNode)) { - // Site container. - PlacesUtils.bhistory.removePagesFromHost(aContainerNode.title, true); - } - else if (PlacesUtils.nodeIsDay(aContainerNode)) { - // Day container. - let query = aContainerNode.getQueries()[0]; - let beginTime = query.beginTime; - let endTime = query.endTime; - NS_ASSERT(query && beginTime && endTime, - "A valid date container query should exist!"); - // We want to exclude beginTime from the removal because - // removePagesByTimeframe includes both extremes, while date containers - // exclude the lower extreme. So, if we would not exclude it, we would - // end up removing more history than requested. - PlacesUtils.bhistory.removePagesByTimeframe(beginTime + 1, endTime); - } - }, - - /** - * Removes the selection - * @param aTxnName - * A name for the transaction if this is being performed - * as part of another operation. - */ - remove: function PC_remove(aTxnName) { - if (!this._hasRemovableSelection()) - return; - - NS_ASSERT(aTxnName !== undefined, "Must supply Transaction Name"); - - var root = this._view.result.root; - - if (PlacesUtils.nodeIsFolder(root)) - this._removeRowsFromBookmarks(aTxnName); - else if (PlacesUtils.nodeIsQuery(root)) { - var queryType = PlacesUtils.asQuery(root).queryOptions.queryType; - if (queryType == Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS) - this._removeRowsFromBookmarks(aTxnName); - else if (queryType == Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) - this._removeRowsFromHistory(); - else - NS_ASSERT(false, "implement support for QUERY_TYPE_UNIFIED"); - } - else - NS_ASSERT(false, "unexpected root"); - }, - - /** - * Fills a DataTransfer object with the content of the selection that can be - * dropped elsewhere. - * @param aEvent - * The dragstart event. - */ - setDataTransfer: function PC_setDataTransfer(aEvent) { - let dt = aEvent.dataTransfer; - let doCopy = ["copyLink", "copy", "link"].indexOf(dt.effectAllowed) != -1; - - let result = this._view.result; - let didSuppressNotifications = result.suppressNotifications; - if (!didSuppressNotifications) - result.suppressNotifications = true; - - function addData(type, index, feedURI) { - let wrapNode = PlacesUtils.wrapNode(node, type, feedURI); - dt.mozSetDataAt(type, wrapNode, index); - } - - function addURIData(index, feedURI) { - addData(PlacesUtils.TYPE_X_MOZ_URL, index, feedURI); - addData(PlacesUtils.TYPE_UNICODE, index, feedURI); - addData(PlacesUtils.TYPE_HTML, index, feedURI); - } - - try { - let nodes = this._view.draggableSelection; - for (let i = 0; i < nodes.length; ++i) { - var node = nodes[i]; - - // This order is _important_! It controls how this and other - // applications select data to be inserted based on type. - addData(PlacesUtils.TYPE_X_MOZ_PLACE, i); - - // Drop the feed uri for livemark containers - let livemarkInfo = this.getCachedLivemarkInfo(node); - if (livemarkInfo) { - addURIData(i, livemarkInfo.feedURI.spec); - } - else if (node.uri) { - addURIData(i); - } - } - } - finally { - if (!didSuppressNotifications) - result.suppressNotifications = false; - } - }, - - get clipboardAction () { - let action = {}; - let actionOwner; - try { - let xferable = Cc["@mozilla.org/widget/transferable;1"]. - createInstance(Ci.nsITransferable); - xferable.init(null); - xferable.addDataFlavor(PlacesUtils.TYPE_X_MOZ_PLACE_ACTION) - this.clipboard.getData(xferable, Ci.nsIClipboard.kGlobalClipboard); - xferable.getTransferData(PlacesUtils.TYPE_X_MOZ_PLACE_ACTION, action, {}); - [action, actionOwner] = - action.value.QueryInterface(Ci.nsISupportsString).data.split(","); - } catch(ex) { - // Paste from external sources don't have any associated action, just - // fallback to a copy action. - return "copy"; - } - // For cuts also check who inited the action, since cuts across different - // instances should instead be handled as copies (The sources are not - // available for this instance). - if (action == "cut" && actionOwner != this.profileName) - action = "copy"; - - return action; - }, - - _releaseClipboardOwnership: function PC__releaseClipboardOwnership() { - if (this.cutNodes.length > 0) { - // This clears the logical clipboard, doesn't remove data. - this.clipboard.emptyClipboard(Ci.nsIClipboard.kGlobalClipboard); - } - }, - - _clearClipboard: function PC__clearClipboard() { - let xferable = Cc["@mozilla.org/widget/transferable;1"]. - createInstance(Ci.nsITransferable); - xferable.init(null); - // Empty transferables may cause crashes, so just add an unknown type. - const TYPE = "text/x-moz-place-empty"; - xferable.addDataFlavor(TYPE); - xferable.setTransferData(TYPE, PlacesUtils.toISupportsString(""), 0); - this.clipboard.setData(xferable, null, Ci.nsIClipboard.kGlobalClipboard); - }, - - _populateClipboard: function PC__populateClipboard(aNodes, aAction) { - // This order is _important_! It controls how this and other applications - // select data to be inserted based on type. - let contents = [ - { type: PlacesUtils.TYPE_X_MOZ_PLACE, entries: [] }, - { type: PlacesUtils.TYPE_X_MOZ_URL, entries: [] }, - { type: PlacesUtils.TYPE_HTML, entries: [] }, - { type: PlacesUtils.TYPE_UNICODE, entries: [] }, - ]; - - // Avoid handling descendants of a copied node, the transactions take care - // of them automatically. - let copiedFolders = []; - aNodes.forEach(function (node) { - if (this._shouldSkipNode(node, copiedFolders)) - return; - if (PlacesUtils.nodeIsFolder(node)) - copiedFolders.push(node); - - let livemarkInfo = this.getCachedLivemarkInfo(node); - let feedURI = livemarkInfo && livemarkInfo.feedURI.spec; - - contents.forEach(function (content) { - content.entries.push( - PlacesUtils.wrapNode(node, content.type, feedURI) - ); - }); - }, this); - - function addData(type, data) { - xferable.addDataFlavor(type); - xferable.setTransferData(type, PlacesUtils.toISupportsString(data), - data.length * 2); - } - - let xferable = Cc["@mozilla.org/widget/transferable;1"]. - createInstance(Ci.nsITransferable); - xferable.init(null); - let hasData = false; - // This order matters here! It controls how this and other applications - // select data to be inserted based on type. - contents.forEach(function (content) { - if (content.entries.length > 0) { - hasData = true; - let glue = - content.type == PlacesUtils.TYPE_X_MOZ_PLACE ? "," : PlacesUtils.endl; - addData(content.type, content.entries.join(glue)); - } - }); - - // Track the exected action in the xferable. This must be the last flavor - // since it's the least preferred one. - // Enqueue a unique instance identifier to distinguish operations across - // concurrent instances of the application. - addData(PlacesUtils.TYPE_X_MOZ_PLACE_ACTION, aAction + "," + this.profileName); - - if (hasData) { - this.clipboard.setData(xferable, - this.cutNodes.length > 0 ? this : null, - Ci.nsIClipboard.kGlobalClipboard); - } - }, - - _cutNodes: [], - get cutNodes() this._cutNodes, - set cutNodes(aNodes) { - let self = this; - function updateCutNodes(aValue) { - self._cutNodes.forEach(function (aNode) { - self._view.toggleCutNode(aNode, aValue); - }); - } - - updateCutNodes(false); - this._cutNodes = aNodes; - updateCutNodes(true); - return aNodes; - }, - - /** - * Copy Bookmarks and Folders to the clipboard - */ - copy: function PC_copy() { - let result = this._view.result; - let didSuppressNotifications = result.suppressNotifications; - if (!didSuppressNotifications) - result.suppressNotifications = true; - try { - this._populateClipboard(this._view.selectedNodes, "copy"); - } - finally { - if (!didSuppressNotifications) - result.suppressNotifications = false; - } - }, - - /** - * Cut Bookmarks and Folders to the clipboard - */ - cut: function PC_cut() { - let result = this._view.result; - let didSuppressNotifications = result.suppressNotifications; - if (!didSuppressNotifications) - result.suppressNotifications = true; - try { - this._populateClipboard(this._view.selectedNodes, "cut"); - this.cutNodes = this._view.selectedNodes; - } - finally { - if (!didSuppressNotifications) - result.suppressNotifications = false; - } - }, - - /** - * Paste Bookmarks and Folders from the clipboard - */ - paste: function PC_paste() { - // No reason to proceed if there isn't a valid insertion point. - let ip = this._view.insertionPoint; - if (!ip) - throw Cr.NS_ERROR_NOT_AVAILABLE; - - let action = this.clipboardAction; - - let xferable = Cc["@mozilla.org/widget/transferable;1"]. - createInstance(Ci.nsITransferable); - xferable.init(null); - // This order matters here! It controls the preferred flavors for this - // paste operation. - [ PlacesUtils.TYPE_X_MOZ_PLACE, - PlacesUtils.TYPE_X_MOZ_URL, - PlacesUtils.TYPE_UNICODE, - ].forEach(function (type) xferable.addDataFlavor(type)); - - this.clipboard.getData(xferable, Ci.nsIClipboard.kGlobalClipboard); - - // Now get the clipboard contents, in the best available flavor. - let data = {}, type = {}, items = []; - try { - xferable.getAnyTransferData(type, data, {}); - data = data.value.QueryInterface(Ci.nsISupportsString).data; - type = type.value; - items = PlacesUtils.unwrapNodes(data, type); - } catch(ex) { - // No supported data exists or nodes unwrap failed, just bail out. - return; - } - - let transactions = []; - let insertionIndex = ip.index; - for (let i = 0; i < items.length; ++i) { - if (ip.isTag) { - // Pasting into a tag container means tagging the item, regardless of - // the requested action. - let tagTxn = new PlacesTagURITransaction(NetUtil.newURI(items[i].uri), - [ip.itemId]); - transactions.push(tagTxn); - continue; - } - - // Adjust index to make sure items are pasted in the correct position. - // If index is DEFAULT_INDEX, items are just appended. - if (ip.index != PlacesUtils.bookmarks.DEFAULT_INDEX) - insertionIndex = ip.index + i; - - transactions.push( - PlacesUIUtils.makeTransaction(items[i], type, ip.itemId, - insertionIndex, action == "copy") - ); - } - - let aggregatedTxn = new PlacesAggregatedTransaction("Paste", transactions); - PlacesUtils.transactionManager.doTransaction(aggregatedTxn); - - // Cut/past operations are not repeatable, so clear the clipboard. - if (action == "cut") { - this._clearClipboard(); - } - - // Select the pasted items, they should be consecutive. - let insertedNodeIds = []; - for (let i = 0; i < transactions.length; ++i) { - insertedNodeIds.push( - PlacesUtils.bookmarks.getIdForItemAt(ip.itemId, ip.index + i) - ); - } - if (insertedNodeIds.length > 0) - this._view.selectItems(insertedNodeIds, false); - }, - - /** - * Cache the livemark info for a node. This allows the controller and the - * views to treat the given node as a livemark. - * @param aNode - * a places result node. - * @param aLivemarkInfo - * a mozILivemarkInfo object. - */ - cacheLivemarkInfo: function PC_cacheLivemarkInfo(aNode, aLivemarkInfo) { - this._cachedLivemarkInfoObjects.set(aNode, aLivemarkInfo); - }, - - /** - * Returns whether or not there's cached mozILivemarkInfo object for a node. - * @param aNode - * a places result node. - * @return true if there's a cached mozILivemarkInfo object for - * aNode, false otherwise. - */ - hasCachedLivemarkInfo: function PC_hasCachedLivemarkInfo(aNode) - this._cachedLivemarkInfoObjects.has(aNode), - - /** - * Returns the cached livemark info for a node, if set by cacheLivemarkInfo, - * null otherwise. - * @param aNode - * a places result node. - * @return the mozILivemarkInfo object for aNode, if set, null otherwise. - */ - getCachedLivemarkInfo: function PC_getCachedLivemarkInfo(aNode) - this._cachedLivemarkInfoObjects.get(aNode, null) -}; - -/** - * Handles drag and drop operations for views. Note that this is view agnostic! - * You should not use PlacesController._view within these methods, since - * the view that the item(s) have been dropped on was not necessarily active. - * Drop functions are passed the view that is being dropped on. - */ -var PlacesControllerDragHelper = { - /** - * DOM Element currently being dragged over - */ - currentDropTarget: null, - - /** - * Determines if the mouse is currently being dragged over a child node of - * this menu. This is necessary so that the menu doesn't close while the - * mouse is dragging over one of its submenus - * @param node - * The container node - * @returns true if the user is dragging over a node within the hierarchy of - * the container, false otherwise. - */ - draggingOverChildNode: function PCDH_draggingOverChildNode(node) { - let currentNode = this.currentDropTarget; - while (currentNode) { - if (currentNode == node) - return true; - currentNode = currentNode.parentNode; - } - return false; - }, - - /** - * @returns The current active drag session. Returns null if there is none. - */ - getSession: function PCDH__getSession() { - return this.dragService.getCurrentSession(); - }, - - /** - * Extract the first accepted flavor from a list of flavors. - * @param aFlavors - * The flavors list of type nsIDOMDOMStringList. - */ - getFirstValidFlavor: function PCDH_getFirstValidFlavor(aFlavors) { - for (let i = 0; i < aFlavors.length; i++) { - if (this.GENERIC_VIEW_DROP_TYPES.indexOf(aFlavors[i]) != -1) - return aFlavors[i]; - } - - // If no supported flavor is found, check if data includes text/plain - // contents. If so, request them as text/unicode, a conversion will happen - // automatically. - if (aFlavors.contains("text/plain")) { - return PlacesUtils.TYPE_UNICODE; - } - - return null; - }, - - /** - * Determines whether or not the data currently being dragged can be dropped - * on a places view. - * @param ip - * The insertion point where the items should be dropped. - */ - canDrop: function PCDH_canDrop(ip, dt) { - let dropCount = dt.mozItemCount; - - // Check every dragged item. - for (let i = 0; i < dropCount; i++) { - let flavor = this.getFirstValidFlavor(dt.mozTypesAt(i)); - if (!flavor) - return false; - - // Urls can be dropped on any insertionpoint. - // XXXmano: remember that this method is called for each dragover event! - // Thus we shouldn't use unwrapNodes here at all if possible. - // I think it would be OK to accept bogus data here (e.g. text which was - // somehow wrapped as TAB_DROP_TYPE, this is not in our control, and - // will just case the actual drop to be a no-op), and only rule out valid - // expected cases, which are either unsupported flavors, or items which - // cannot be dropped in the current insertionpoint. The last case will - // likely force us to use unwrapNodes for the private data types of - // places. - if (flavor == TAB_DROP_TYPE) - continue; - - let data = dt.mozGetDataAt(flavor, i); - let dragged; - try { - dragged = PlacesUtils.unwrapNodes(data, flavor)[0]; - } - catch (e) { - return false; - } - - // Only bookmarks and urls can be dropped into tag containers. - if (ip.isTag && ip.orientation == Ci.nsITreeView.DROP_ON && - dragged.type != PlacesUtils.TYPE_X_MOZ_URL && - (dragged.type != PlacesUtils.TYPE_X_MOZ_PLACE || - (dragged.uri && dragged.uri.startsWith("place:")) )) - return false; - - // The following loop disallows the dropping of a folder on itself or - // on any of its descendants. - if (dragged.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER || - (dragged.uri && dragged.uri.startsWith("place:")) ) { - let parentId = ip.itemId; - while (parentId != PlacesUtils.placesRootId) { - if (dragged.concreteId == parentId || dragged.id == parentId) - return false; - parentId = PlacesUtils.bookmarks.getFolderIdForItem(parentId); - } - } - } - return true; - }, - - - /** - * Determines if a node can be moved. - * - * @param aNode - * A nsINavHistoryResultNode node. - * @returns True if the node can be moved, false otherwise. - */ - canMoveNode: - function PCDH_canMoveNode(aNode) { - // Only bookmark items are movable. - if (aNode.itemId == -1) - return false; - - // Once tags and bookmarked are divorced, the tag-query check should be - // removed. - let parentNode = aNode.parent; - return parentNode != null && - !(PlacesUtils.nodeIsFolder(parentNode) && - PlacesUIUtils.isContentsReadOnly(parentNode)) && - !PlacesUtils.nodeIsTagQuery(parentNode); - }, - - /** - * Handles the drop of one or more items onto a view. - * @param insertionPoint - * The insertion point where the items should be dropped - */ - onDrop: function PCDH_onDrop(insertionPoint, dt) { - let doCopy = ["copy", "link"].indexOf(dt.dropEffect) != -1; - - let transactions = []; - let dropCount = dt.mozItemCount; - let movedCount = 0; - for (let i = 0; i < dropCount; ++i) { - let flavor = this.getFirstValidFlavor(dt.mozTypesAt(i)); - if (!flavor) - return; - - let data = dt.mozGetDataAt(flavor, i); - let unwrapped; - if (flavor != TAB_DROP_TYPE) { - // There's only ever one in the D&D case. - unwrapped = PlacesUtils.unwrapNodes(data, flavor)[0]; - } - else if (data instanceof XULElement && data.localName == "tab" && - data.ownerDocument.defaultView instanceof ChromeWindow) { - let uri = data.linkedBrowser.currentURI; - let spec = uri ? uri.spec : "about:blank"; - let title = data.label; - unwrapped = { uri: spec, - title: data.label, - type: PlacesUtils.TYPE_X_MOZ_URL}; - } - else - throw("bogus data was passed as a tab"); - - let index = insertionPoint.index; - - // Adjust insertion index to prevent reversal of dragged items. When you - // drag multiple elts upward: need to increment index or each successive - // elt will be inserted at the same index, each above the previous. - let dragginUp = insertionPoint.itemId == unwrapped.parent && - index < PlacesUtils.bookmarks.getItemIndex(unwrapped.id); - if (index != -1 && dragginUp) - index += movedCount++; - - // If dragging over a tag container we should tag the item. - if (insertionPoint.isTag && - insertionPoint.orientation == Ci.nsITreeView.DROP_ON) { - let uri = NetUtil.newURI(unwrapped.uri); - let tagItemId = insertionPoint.itemId; - let tagTxn = new PlacesTagURITransaction(uri, [tagItemId]); - transactions.push(tagTxn); - } - else { - transactions.push(PlacesUIUtils.makeTransaction(unwrapped, - flavor, insertionPoint.itemId, - index, doCopy)); - } - } - - let txn = new PlacesAggregatedTransaction("DropItems", transactions); - PlacesUtils.transactionManager.doTransaction(txn); - }, - - /** - * Checks if we can insert into a container. - * @param aContainer - * The container were we are want to drop - */ - disallowInsertion: function(aContainer) { - NS_ASSERT(aContainer, "empty container"); - // Allow dropping into Tag containers and editable folders. - return !PlacesUtils.nodeIsTagQuery(aContainer) && - (!PlacesUtils.nodeIsFolder(aContainer) || - PlacesUIUtils.isContentsReadOnly(aContainer)); - }, - - placesFlavors: [PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER, - PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR, - PlacesUtils.TYPE_X_MOZ_PLACE], - - // The order matters. - GENERIC_VIEW_DROP_TYPES: [PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER, - PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR, - PlacesUtils.TYPE_X_MOZ_PLACE, - PlacesUtils.TYPE_X_MOZ_URL, - TAB_DROP_TYPE, - PlacesUtils.TYPE_UNICODE], -}; - - -XPCOMUtils.defineLazyServiceGetter(PlacesControllerDragHelper, "dragService", - "@mozilla.org/widget/dragservice;1", - "nsIDragService"); - -function goUpdatePlacesCommands() { - // Get the controller for one of the places commands. - var placesController = doGetPlacesControllerForCommand("placesCmd_open"); - function updatePlacesCommand(aCommand) { - goSetCommandEnabled(aCommand, placesController && - placesController.isCommandEnabled(aCommand)); - } - - updatePlacesCommand("placesCmd_open"); - updatePlacesCommand("placesCmd_open:window"); - updatePlacesCommand("placesCmd_open:privatewindow"); - updatePlacesCommand("placesCmd_open:tab"); - updatePlacesCommand("placesCmd_new:folder"); - updatePlacesCommand("placesCmd_new:bookmark"); - updatePlacesCommand("placesCmd_new:livemark"); - updatePlacesCommand("placesCmd_new:separator"); - updatePlacesCommand("placesCmd_show:info"); - updatePlacesCommand("placesCmd_moveBookmarks"); - updatePlacesCommand("placesCmd_reload"); - updatePlacesCommand("placesCmd_sortBy:name"); - updatePlacesCommand("placesCmd_openParentFolder"); - updatePlacesCommand("placesCmd_cut"); - updatePlacesCommand("placesCmd_copy"); - updatePlacesCommand("placesCmd_paste"); - updatePlacesCommand("placesCmd_delete"); -} - -function doGetPlacesControllerForCommand(aCommand) -{ - // A context menu may be built for non-focusable views. Thus, we first try - // to look for a view associated with document.popupNode - let popupNode; - try { - popupNode = document.popupNode; - } catch (e) { - // The document went away (bug 797307). - return null; - } - if (popupNode) { - let view = PlacesUIUtils.getViewForNode(popupNode); - if (view && view._contextMenuShown) - return view.controllers.getControllerForCommand(aCommand); - } - - // When we're not building a context menu, only focusable views - // are possible. Thus, we can safely use the command dispatcher. - let controller = top.document.commandDispatcher - .getControllerForCommand(aCommand); - if (controller) - return controller; - - return null; -} - -function goDoPlacesCommand(aCommand) -{ - let controller = doGetPlacesControllerForCommand(aCommand); - if (controller && controller.isCommandEnabled(aCommand)) - controller.doCommand(aCommand); -} - diff --git a/components/places/content/downloadsViewOverlay.xul b/components/places/content/downloadsViewOverlay.xul deleted file mode 100644 index 8e2d775..0000000 --- a/components/places/content/downloadsViewOverlay.xul +++ /dev/null @@ -1,47 +0,0 @@ -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<?xul-overlay href="chrome://browser/content/downloads/allDownloadsViewOverlay.xul"?> - -<!DOCTYPE overlay [ -<!ENTITY % downloadsDTD SYSTEM "chrome://browser/locale/downloads/downloads.dtd"> -%downloadsDTD; -]> - -<overlay id="downloadsViewOverlay" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - - <script type="application/javascript"><![CDATA[ - const DOWNLOADS_QUERY = "place:transition=" + - Components.interfaces.nsINavHistoryService.TRANSITION_DOWNLOAD + - "&sort=" + - Components.interfaces.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING; - - ContentArea.setContentViewForQueryString(DOWNLOADS_QUERY, - function() new DownloadsPlacesView(document.getElementById("downloadsRichListBox"), false), - { showDetailsPane: false, - toolbarSet: "back-button, forward-button, organizeButton, clearDownloadsButton, libraryToolbarSpacer, searchFilter" }); - ]]></script> - - <window id="places"> - <commandset id="downloadCommands"/> - <menupopup id="downloadsContextMenu"/> - </window> - - <deck id="placesViewsDeck"> - <richlistbox id="downloadsRichListBox"/> - </deck> - - <toolbar id="placesToolbar"> - <toolbarbutton id="clearDownloadsButton" -#ifdef XP_MACOSX - class="tabbable" -#endif - insertbefore="libraryToolbarSpacer" - label="&clearDownloadsButton.label;" - command="downloadsCmd_clearDownloads" - tooltiptext="&clearDownloadsButton.tooltip;"/> - </toolbar> - -</overlay> diff --git a/components/places/content/editBookmarkOverlay.js b/components/places/content/editBookmarkOverlay.js deleted file mode 100644 index 69d7d32..0000000 --- a/components/places/content/editBookmarkOverlay.js +++ /dev/null @@ -1,1063 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -const LAST_USED_ANNO = "bookmarkPropertiesDialog/folderLastUsed"; -const MAX_FOLDER_ITEM_IN_MENU_LIST = 5; - -var gEditItemOverlay = { - _uri: null, - _itemId: -1, - _itemIds: [], - _uris: [], - _tags: [], - _allTags: [], - _keyword: null, - _multiEdit: false, - _itemType: -1, - _readOnly: false, - _hiddenRows: [], - _onPanelReady: false, - _observersAdded: false, - _staticFoldersListBuilt: false, - _initialized: false, - _titleOverride: "", - - // the first field which was edited after this panel was initialized for - // a certain item - _firstEditedField: "", - - get itemId() { - return this._itemId; - }, - - get uri() { - return this._uri; - }, - - get multiEdit() { - return this._multiEdit; - }, - - /** - * Determines the initial data for the item edited or added by this dialog - */ - _determineInfo: function EIO__determineInfo(aInfo) { - // hidden rows - if (aInfo && aInfo.hiddenRows) - this._hiddenRows = aInfo.hiddenRows; - else - this._hiddenRows.splice(0, this._hiddenRows.length); - // force-read-only - this._readOnly = aInfo && aInfo.forceReadOnly; - this._titleOverride = aInfo && aInfo.titleOverride ? aInfo.titleOverride - : ""; - this._onPanelReady = aInfo && aInfo.onPanelReady; - }, - - _showHideRows: function EIO__showHideRows() { - var isBookmark = this._itemId != -1 && - this._itemType == Ci.nsINavBookmarksService.TYPE_BOOKMARK; - var isQuery = false; - if (this._uri) - isQuery = this._uri.schemeIs("place"); - - this._element("nameRow").collapsed = this._hiddenRows.indexOf("name") != -1; - this._element("folderRow").collapsed = - this._hiddenRows.indexOf("folderPicker") != -1 || this._readOnly; - this._element("tagsRow").collapsed = !this._uri || - this._hiddenRows.indexOf("tags") != -1 || isQuery; - // Collapse the tag selector if the item does not accept tags. - if (!this._element("tagsSelectorRow").collapsed && - this._element("tagsRow").collapsed) - this.toggleTagsSelector(); - this._element("descriptionRow").collapsed = - this._hiddenRows.indexOf("description") != -1 || this._readOnly; - this._element("keywordRow").collapsed = !isBookmark || this._readOnly || - this._hiddenRows.indexOf("keyword") != -1 || isQuery; - this._element("locationRow").collapsed = !(this._uri && !isQuery) || - this._hiddenRows.indexOf("location") != -1; - this._element("loadInSidebarCheckbox").collapsed = !isBookmark || isQuery || - this._readOnly || this._hiddenRows.indexOf("loadInSidebar") != -1; - this._element("feedLocationRow").collapsed = !this._isLivemark || - this._hiddenRows.indexOf("feedLocation") != -1; - this._element("siteLocationRow").collapsed = !this._isLivemark || - this._hiddenRows.indexOf("siteLocation") != -1; - this._element("selectionCount").hidden = !this._multiEdit; - }, - - /** - * Initialize the panel - * @param aFor - * Either a places-itemId (of a bookmark, folder or a live bookmark), - * an array of itemIds (used for bulk tagging), or a URI object (in - * which case, the panel would be initialized in read-only mode). - * @param [optional] aInfo - * JS object which stores additional info for the panel - * initialization. The following properties may bet set: - * * hiddenRows (Strings array): list of rows to be hidden regardless - * of the item edited. Possible values: "title", "location", - * "description", "keyword", "loadInSidebar", "feedLocation", - * "siteLocation", folderPicker" - * * forceReadOnly - set this flag to initialize the panel to its - * read-only (view) mode even if the given item is editable. - */ - initPanel: function EIO_initPanel(aFor, aInfo) { - // For sanity ensure that the implementer has uninited the panel before - // trying to init it again, or we could end up leaking due to observers. - if (this._initialized) - this.uninitPanel(false); - - var aItemIdList; - if (Array.isArray(aFor)) { - aItemIdList = aFor; - aFor = aItemIdList[0]; - } - else if (this._multiEdit) { - this._multiEdit = false; - this._tags = []; - this._uris = []; - this._allTags = []; - this._itemIds = []; - this._element("selectionCount").hidden = true; - } - - this._folderMenuList = this._element("folderMenuList"); - this._folderTree = this._element("folderTree"); - - this._determineInfo(aInfo); - if (aFor instanceof Ci.nsIURI) { - this._itemId = -1; - this._uri = aFor; - this._readOnly = true; - } - else { - this._itemId = aFor; - // We can't store information on invalid itemIds. - this._readOnly = this._readOnly || this._itemId == -1; - - var containerId = PlacesUtils.bookmarks.getFolderIdForItem(this._itemId); - this._itemType = PlacesUtils.bookmarks.getItemType(this._itemId); - if (this._itemType == Ci.nsINavBookmarksService.TYPE_BOOKMARK) { - this._uri = PlacesUtils.bookmarks.getBookmarkURI(this._itemId); - this._keyword = PlacesUtils.bookmarks.getKeywordForBookmark(this._itemId); - this._initTextField("keywordField", this._keyword); - this._element("loadInSidebarCheckbox").checked = - PlacesUtils.annotations.itemHasAnnotation(this._itemId, - PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO); - } - else { - this._uri = null; - this._isLivemark = false; - PlacesUtils.livemarks.getLivemark({id: this._itemId }) - .then(aLivemark => { - this._isLivemark = true; - this._initTextField("feedLocationField", aLivemark.feedURI.spec, true); - this._initTextField("siteLocationField", aLivemark.siteURI ? aLivemark.siteURI.spec : "", true); - this._showHideRows(); - }, () => undefined); - } - - // folder picker - this._initFolderMenuList(containerId); - - // description field - this._initTextField("descriptionField", - PlacesUIUtils.getItemDescription(this._itemId)); - } - - if (this._itemId == -1 || - this._itemType == Ci.nsINavBookmarksService.TYPE_BOOKMARK) { - this._isLivemark = false; - - this._initTextField("locationField", this._uri.spec); - if (!aItemIdList) { - var tags = PlacesUtils.tagging.getTagsForURI(this._uri).join(", "); - this._initTextField("tagsField", tags, false); - } - else { - this._multiEdit = true; - this._allTags = []; - this._itemIds = aItemIdList; - for (var i = 0; i < aItemIdList.length; i++) { - if (aItemIdList[i] instanceof Ci.nsIURI) { - this._uris[i] = aItemIdList[i]; - this._itemIds[i] = -1; - } - else - this._uris[i] = PlacesUtils.bookmarks.getBookmarkURI(this._itemIds[i]); - this._tags[i] = PlacesUtils.tagging.getTagsForURI(this._uris[i]); - } - this._allTags = this._getCommonTags(); - this._initTextField("tagsField", this._allTags.join(", "), false); - this._element("itemsCountText").value = - PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel", - this._itemIds.length, - [this._itemIds.length]); - } - - // tags selector - this._rebuildTagsSelectorList(); - } - - // name picker - this._initNamePicker(); - - this._showHideRows(); - - // observe changes - if (!this._observersAdded) { - // Single bookmarks observe any change. History entries and multiEdit - // observe only tags changes, through bookmarks. - if (this._itemId != -1 || this._uri || this._multiEdit) - PlacesUtils.bookmarks.addObserver(this, false); - - this._element("namePicker").addEventListener("blur", this); - this._element("locationField").addEventListener("blur", this); - this._element("tagsField").addEventListener("blur", this); - this._element("keywordField").addEventListener("blur", this); - this._element("descriptionField").addEventListener("blur", this); - window.addEventListener("unload", this, false); - this._observersAdded = true; - } - - let focusElement = () => { - this._initialized = true; - }; - - if (this._onPanelReady) { - this._onPanelReady(focusElement); - } else { - focusElement(); - } - }, - - /** - * Finds tags that are in common among this._tags entries that track tags - * for each selected uri. - * The tags arrays should be kept up-to-date for this to work properly. - * - * @return array of common tags for the selected uris. - */ - _getCommonTags: function() { - return this._tags[0].filter( - function (aTag) this._tags.every( - function (aTags) aTags.indexOf(aTag) != -1 - ), this - ); - }, - - _initTextField: function(aTextFieldId, aValue, aReadOnly) { - var field = this._element(aTextFieldId); - field.readOnly = aReadOnly !== undefined ? aReadOnly : this._readOnly; - - if (field.value != aValue) { - field.value = aValue; - this._editorTransactionManagerClear(field); - } - }, - - /** - * Appends a menu-item representing a bookmarks folder to a menu-popup. - * @param aMenupopup - * The popup to which the menu-item should be added. - * @param aFolderId - * The identifier of the bookmarks folder. - * @return the new menu item. - */ - _appendFolderItemToMenupopup: - function EIO__appendFolderItemToMenuList(aMenupopup, aFolderId) { - // First make sure the folders-separator is visible - this._element("foldersSeparator").hidden = false; - - var folderMenuItem = document.createElement("menuitem"); - var folderTitle = PlacesUtils.bookmarks.getItemTitle(aFolderId) - folderMenuItem.folderId = aFolderId; - folderMenuItem.setAttribute("label", folderTitle); - folderMenuItem.className = "menuitem-iconic folder-icon"; - aMenupopup.appendChild(folderMenuItem); - return folderMenuItem; - }, - - _initFolderMenuList: function EIO__initFolderMenuList(aSelectedFolder) { - // clean up first - var menupopup = this._folderMenuList.menupopup; - while (menupopup.childNodes.length > 6) - menupopup.removeChild(menupopup.lastChild); - - const bms = PlacesUtils.bookmarks; - const annos = PlacesUtils.annotations; - - // Build the static list - var unfiledItem = this._element("unfiledRootItem"); - if (!this._staticFoldersListBuilt) { - unfiledItem.label = bms.getItemTitle(PlacesUtils.unfiledBookmarksFolderId); - unfiledItem.folderId = PlacesUtils.unfiledBookmarksFolderId; - var bmMenuItem = this._element("bmRootItem"); - bmMenuItem.label = bms.getItemTitle(PlacesUtils.bookmarksMenuFolderId); - bmMenuItem.folderId = PlacesUtils.bookmarksMenuFolderId; - var toolbarItem = this._element("toolbarFolderItem"); - toolbarItem.label = bms.getItemTitle(PlacesUtils.toolbarFolderId); - toolbarItem.folderId = PlacesUtils.toolbarFolderId; - this._staticFoldersListBuilt = true; - } - - // List of recently used folders: - var folderIds = annos.getItemsWithAnnotation(LAST_USED_ANNO); - - /** - * The value of the LAST_USED_ANNO annotation is the time (in the form of - * Date.getTime) at which the folder has been last used. - * - * First we build the annotated folders array, each item has both the - * folder identifier and the time at which it was last-used by this dialog - * set. Then we sort it descendingly based on the time field. - */ - this._recentFolders = []; - for (var i = 0; i < folderIds.length; i++) { - var lastUsed = annos.getItemAnnotation(folderIds[i], LAST_USED_ANNO); - this._recentFolders.push({ folderId: folderIds[i], lastUsed: lastUsed }); - } - this._recentFolders.sort(function(a, b) { - if (b.lastUsed < a.lastUsed) - return -1; - if (b.lastUsed > a.lastUsed) - return 1; - return 0; - }); - - var numberOfItems = Math.min(MAX_FOLDER_ITEM_IN_MENU_LIST, - this._recentFolders.length); - for (var i = 0; i < numberOfItems; i++) { - this._appendFolderItemToMenupopup(menupopup, - this._recentFolders[i].folderId); - } - - var defaultItem = this._getFolderMenuItem(aSelectedFolder); - this._folderMenuList.selectedItem = defaultItem; - - // Set a selectedIndex attribute to show special icons - this._folderMenuList.setAttribute("selectedIndex", - this._folderMenuList.selectedIndex); - - // Hide the folders-separator if no folder is annotated as recently-used - this._element("foldersSeparator").hidden = (menupopup.childNodes.length <= 6); - this._folderMenuList.disabled = this._readOnly; - }, - - QueryInterface: function EIO_QueryInterface(aIID) { - if (aIID.equals(Ci.nsIDOMEventListener) || - aIID.equals(Ci.nsINavBookmarkObserver) || - aIID.equals(Ci.nsISupports)) - return this; - - throw Cr.NS_ERROR_NO_INTERFACE; - }, - - _element: function EIO__element(aID) { - return document.getElementById("editBMPanel_" + aID); - }, - - _editorTransactionManagerClear: function EIO__editorTransactionManagerClear(aItem) { - // Clear the editor's undo stack - let transactionManager; - try { - transactionManager = aItem.editor.transactionManager; - } catch (e) { - // When retrieving the transaction manager, editor may be null resulting - // in a TypeError. Additionally, the transaction manager may not - // exist yet, which causes access to it to throw NS_ERROR_FAILURE. - // In either event, the transaction manager doesn't exist it, so we - // don't need to worry about clearing it. - if (!(e instanceof TypeError) && e.result != Cr.NS_ERROR_FAILURE) { - throw e; - } - } - if (transactionManager) { - transactionManager.clear(); - } - }, - - _getItemStaticTitle: function EIO__getItemStaticTitle() { - if (this._titleOverride) - return this._titleOverride; - - let title = ""; - if (this._itemId == -1) { - title = PlacesUtils.history.getPageTitle(this._uri); - } - else { - title = PlacesUtils.bookmarks.getItemTitle(this._itemId); - } - return title; - }, - - _initNamePicker: function EIO_initNamePicker() { - var namePicker = this._element("namePicker"); - namePicker.value = this._getItemStaticTitle(); - namePicker.readOnly = this._readOnly; - this._editorTransactionManagerClear(namePicker); - }, - - uninitPanel: function EIO_uninitPanel(aHideCollapsibleElements) { - if (aHideCollapsibleElements) { - // hide the folder tree if it was previously visible - var folderTreeRow = this._element("folderTreeRow"); - if (!folderTreeRow.collapsed) - this.toggleFolderTreeVisibility(); - - // hide the tag selector if it was previously visible - var tagsSelectorRow = this._element("tagsSelectorRow"); - if (!tagsSelectorRow.collapsed) - this.toggleTagsSelector(); - } - - if (this._observersAdded) { - if (this._itemId != -1 || this._uri || this._multiEdit) - PlacesUtils.bookmarks.removeObserver(this); - - this._element("namePicker").removeEventListener("blur", this); - this._element("locationField").removeEventListener("blur", this); - this._element("tagsField").removeEventListener("blur", this); - this._element("keywordField").removeEventListener("blur", this); - this._element("descriptionField").removeEventListener("blur", this); - - this._observersAdded = false; - } - - this._itemId = -1; - this._uri = null; - this._uris = []; - this._tags = []; - this._allTags = []; - this._itemIds = []; - this._multiEdit = false; - this._firstEditedField = ""; - this._initialized = false; - this._titleOverride = ""; - this._readOnly = false; - }, - - onTagsFieldBlur: function EIO_onTagsFieldBlur() { - if (this._updateTags()) // if anything has changed - this._mayUpdateFirstEditField("tagsField"); - }, - - _updateTags: function EIO__updateTags() { - if (this._multiEdit) - return this._updateMultipleTagsForItems(); - return this._updateSingleTagForItem(); - }, - - _updateSingleTagForItem: function EIO__updateSingleTagForItem() { - var currentTags = PlacesUtils.tagging.getTagsForURI(this._uri); - var tags = this._getTagsArrayFromTagField(); - if (tags.length > 0 || currentTags.length > 0) { - var tagsToRemove = []; - var tagsToAdd = []; - var txns = []; - for (var i = 0; i < currentTags.length; i++) { - if (tags.indexOf(currentTags[i]) == -1) - tagsToRemove.push(currentTags[i]); - } - for (var i = 0; i < tags.length; i++) { - if (currentTags.indexOf(tags[i]) == -1) - tagsToAdd.push(tags[i]); - } - - if (tagsToRemove.length > 0) { - let untagTxn = new PlacesUntagURITransaction(this._uri, tagsToRemove); - txns.push(untagTxn); - } - if (tagsToAdd.length > 0) { - let tagTxn = new PlacesTagURITransaction(this._uri, tagsToAdd); - txns.push(tagTxn); - } - - if (txns.length > 0) { - let aggregate = new PlacesAggregatedTransaction("Update tags", txns); - PlacesUtils.transactionManager.doTransaction(aggregate); - - // Ensure the tagsField is in sync, clean it up from empty tags - var tags = PlacesUtils.tagging.getTagsForURI(this._uri).join(", "); - this._initTextField("tagsField", tags, false); - return true; - } - } - return false; - }, - - /** - * Stores the first-edit field for this dialog, if the passed-in field - * is indeed the first edited field - * @param aNewField - * the id of the field that may be set (without the "editBMPanel_" - * prefix) - */ - _mayUpdateFirstEditField: function EIO__mayUpdateFirstEditField(aNewField) { - // * The first-edit-field behavior is not applied in the multi-edit case - // * if this._firstEditedField is already set, this is not the first field, - // so there's nothing to do - if (this._multiEdit || this._firstEditedField) - return; - - this._firstEditedField = aNewField; - - // set the pref - var prefs = Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefBranch); - prefs.setCharPref("browser.bookmarks.editDialog.firstEditField", aNewField); - }, - - _updateMultipleTagsForItems: function EIO__updateMultipleTagsForItems() { - var tags = this._getTagsArrayFromTagField(); - if (tags.length > 0 || this._allTags.length > 0) { - var tagsToRemove = []; - var tagsToAdd = []; - var txns = []; - for (var i = 0; i < this._allTags.length; i++) { - if (tags.indexOf(this._allTags[i]) == -1) - tagsToRemove.push(this._allTags[i]); - } - for (var i = 0; i < this._tags.length; i++) { - tagsToAdd[i] = []; - for (var j = 0; j < tags.length; j++) { - if (this._tags[i].indexOf(tags[j]) == -1) - tagsToAdd[i].push(tags[j]); - } - } - - if (tagsToAdd.length > 0) { - for (let i = 0; i < this._uris.length; i++) { - if (tagsToAdd[i].length > 0) { - let tagTxn = new PlacesTagURITransaction(this._uris[i], - tagsToAdd[i]); - txns.push(tagTxn); - } - } - } - if (tagsToRemove.length > 0) { - for (let i = 0; i < this._uris.length; i++) { - let untagTxn = new PlacesUntagURITransaction(this._uris[i], - tagsToRemove); - txns.push(untagTxn); - } - } - - if (txns.length > 0) { - let aggregate = new PlacesAggregatedTransaction("Update tags", txns); - PlacesUtils.transactionManager.doTransaction(aggregate); - - this._allTags = tags; - this._tags = []; - for (let i = 0; i < this._uris.length; i++) { - this._tags[i] = PlacesUtils.tagging.getTagsForURI(this._uris[i]); - } - - // Ensure the tagsField is in sync, clean it up from empty tags - this._initTextField("tagsField", tags, false); - return true; - } - } - return false; - }, - - onNamePickerBlur: function EIO_onNamePickerBlur() { - if (this._itemId == -1) - return; - - var namePicker = this._element("namePicker") - - // Here we update either the item title or its cached static title - var newTitle = namePicker.value; - if (!newTitle && - PlacesUtils.bookmarks.getFolderIdForItem(this._itemId) == PlacesUtils.tagsFolderId) { - // We don't allow setting an empty title for a tag, restore the old one. - this._initNamePicker(); - } - else if (this._getItemStaticTitle() != newTitle) { - this._mayUpdateFirstEditField("namePicker"); - let txn = new PlacesEditItemTitleTransaction(this._itemId, newTitle); - PlacesUtils.transactionManager.doTransaction(txn); - } - }, - - onDescriptionFieldBlur: function EIO_onDescriptionFieldBlur() { - var description = this._element("descriptionField").value; - if (description != PlacesUIUtils.getItemDescription(this._itemId)) { - var annoObj = { name : PlacesUIUtils.DESCRIPTION_ANNO, - type : Ci.nsIAnnotationService.TYPE_STRING, - flags : 0, - value : description, - expires: Ci.nsIAnnotationService.EXPIRE_NEVER }; - var txn = new PlacesSetItemAnnotationTransaction(this._itemId, annoObj); - PlacesUtils.transactionManager.doTransaction(txn); - } - }, - - onLocationFieldBlur: function EIO_onLocationFieldBlur() { - var uri; - try { - uri = PlacesUIUtils.createFixedURI(this._element("locationField").value); - } - catch(ex) { return; } - - if (!this._uri.equals(uri)) { - var txn = new PlacesEditBookmarkURITransaction(this._itemId, uri); - PlacesUtils.transactionManager.doTransaction(txn); - this._uri = uri; - } - }, - - onKeywordFieldBlur: function EIO_onKeywordFieldBlur() { - let oldKeyword = this._keyword; - let keyword = this._keyword = this._element("keywordField").value; - if (keyword != oldKeyword) { - let txn = new PlacesEditBookmarkKeywordTransaction(this._itemId, - keyword, - null, - oldKeyword); - PlacesUtils.transactionManager.doTransaction(txn); - } - }, - - onLoadInSidebarCheckboxCommand: - function EIO_onLoadInSidebarCheckboxCommand() { - let annoObj = { name : PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO }; - if (this._element("loadInSidebarCheckbox").checked) - annoObj.value = true; - let txn = new PlacesSetItemAnnotationTransaction(this._itemId, annoObj); - PlacesUtils.transactionManager.doTransaction(txn); - }, - - toggleFolderTreeVisibility: function EIO_toggleFolderTreeVisibility() { - var expander = this._element("foldersExpander"); - var folderTreeRow = this._element("folderTreeRow"); - if (!folderTreeRow.collapsed) { - expander.className = "expander-down"; - expander.setAttribute("tooltiptext", - expander.getAttribute("tooltiptextdown")); - folderTreeRow.collapsed = true; - this._element("chooseFolderSeparator").hidden = - this._element("chooseFolderMenuItem").hidden = false; - } - else { - expander.className = "expander-up" - expander.setAttribute("tooltiptext", - expander.getAttribute("tooltiptextup")); - folderTreeRow.collapsed = false; - - // XXXmano: Ideally we would only do this once, but for some odd reason, - // the editable mode set on this tree, together with its collapsed state - // breaks the view. - const FOLDER_TREE_PLACE_URI = - "place:excludeItems=1&excludeQueries=1&excludeReadOnlyFolders=1&folder=" + - PlacesUIUtils.allBookmarksFolderId; - this._folderTree.place = FOLDER_TREE_PLACE_URI; - - this._element("chooseFolderSeparator").hidden = - this._element("chooseFolderMenuItem").hidden = true; - var currentFolder = this._getFolderIdFromMenuList(); - this._folderTree.selectItems([currentFolder]); - this._folderTree.focus(); - } - }, - - _getFolderIdFromMenuList: - function EIO__getFolderIdFromMenuList() { - var selectedItem = this._folderMenuList.selectedItem; - NS_ASSERT("folderId" in selectedItem, - "Invalid menuitem in the folders-menulist"); - return selectedItem.folderId; - }, - - /** - * Get the corresponding menu-item in the folder-menu-list for a bookmarks - * folder if such an item exists. Otherwise, this creates a menu-item for the - * folder. If the items-count limit (see MAX_FOLDERS_IN_MENU_LIST) is reached, - * the new item replaces the last menu-item. - * @param aFolderId - * The identifier of the bookmarks folder. - */ - _getFolderMenuItem: - function EIO__getFolderMenuItem(aFolderId) { - var menupopup = this._folderMenuList.menupopup; - - for (let i = 0; i < menupopup.childNodes.length; i++) { - if ("folderId" in menupopup.childNodes[i] && - menupopup.childNodes[i].folderId == aFolderId) - return menupopup.childNodes[i]; - } - - // 3 special folders + separator + folder-items-count limit - if (menupopup.childNodes.length == 4 + MAX_FOLDER_ITEM_IN_MENU_LIST) - menupopup.removeChild(menupopup.lastChild); - - return this._appendFolderItemToMenupopup(menupopup, aFolderId); - }, - - onFolderMenuListCommand: function EIO_onFolderMenuListCommand(aEvent) { - // Set a selectedIndex attribute to show special icons - this._folderMenuList.setAttribute("selectedIndex", - this._folderMenuList.selectedIndex); - - if (aEvent.target.id == "editBMPanel_chooseFolderMenuItem") { - // reset the selection back to where it was and expand the tree - // (this menu-item is hidden when the tree is already visible - var container = PlacesUtils.bookmarks.getFolderIdForItem(this._itemId); - var item = this._getFolderMenuItem(container); - this._folderMenuList.selectedItem = item; - // XXXmano HACK: setTimeout 100, otherwise focus goes back to the - // menulist right away - setTimeout(function(self) self.toggleFolderTreeVisibility(), 100, this); - return; - } - - // Move the item - var container = this._getFolderIdFromMenuList(); - if (PlacesUtils.bookmarks.getFolderIdForItem(this._itemId) != container) { - var txn = new PlacesMoveItemTransaction(this._itemId, - container, - PlacesUtils.bookmarks.DEFAULT_INDEX); - PlacesUtils.transactionManager.doTransaction(txn); - - // Mark the containing folder as recently-used if it isn't in the - // static list - if (container != PlacesUtils.unfiledBookmarksFolderId && - container != PlacesUtils.toolbarFolderId && - container != PlacesUtils.bookmarksMenuFolderId) - this._markFolderAsRecentlyUsed(container); - } - - // Update folder-tree selection - var folderTreeRow = this._element("folderTreeRow"); - if (!folderTreeRow.collapsed) { - var selectedNode = this._folderTree.selectedNode; - if (!selectedNode || - PlacesUtils.getConcreteItemId(selectedNode) != container) - this._folderTree.selectItems([container]); - } - }, - - onFolderTreeSelect: function EIO_onFolderTreeSelect() { - var selectedNode = this._folderTree.selectedNode; - - // Disable the "New Folder" button if we cannot create a new folder - this._element("newFolderButton") - .disabled = !this._folderTree.insertionPoint || !selectedNode; - - if (!selectedNode) - return; - - var folderId = PlacesUtils.getConcreteItemId(selectedNode); - if (this._getFolderIdFromMenuList() == folderId) - return; - - var folderItem = this._getFolderMenuItem(folderId); - this._folderMenuList.selectedItem = folderItem; - folderItem.doCommand(); - }, - - _markFolderAsRecentlyUsed: - function EIO__markFolderAsRecentlyUsed(aFolderId) { - var txns = []; - - // Expire old unused recent folders - var anno = this._getLastUsedAnnotationObject(false); - while (this._recentFolders.length > MAX_FOLDER_ITEM_IN_MENU_LIST) { - var folderId = this._recentFolders.pop().folderId; - let annoTxn = new PlacesSetItemAnnotationTransaction(folderId, anno); - txns.push(annoTxn); - } - - // Mark folder as recently used - anno = this._getLastUsedAnnotationObject(true); - let annoTxn = new PlacesSetItemAnnotationTransaction(aFolderId, anno); - txns.push(annoTxn); - - let aggregate = new PlacesAggregatedTransaction("Update last used folders", txns); - PlacesUtils.transactionManager.doTransaction(aggregate); - }, - - /** - * Returns an object which could then be used to set/unset the - * LAST_USED_ANNO annotation for a folder. - * - * @param aLastUsed - * Whether to set or unset the LAST_USED_ANNO annotation. - * @returns an object representing the annotation which could then be used - * with the transaction manager. - */ - _getLastUsedAnnotationObject: - function EIO__getLastUsedAnnotationObject(aLastUsed) { - var anno = { name: LAST_USED_ANNO, - type: Ci.nsIAnnotationService.TYPE_INT32, - flags: 0, - value: aLastUsed ? new Date().getTime() : null, - expires: Ci.nsIAnnotationService.EXPIRE_NEVER }; - - return anno; - }, - - _rebuildTagsSelectorList: function EIO__rebuildTagsSelectorList() { - var tagsSelector = this._element("tagsSelector"); - var tagsSelectorRow = this._element("tagsSelectorRow"); - if (tagsSelectorRow.collapsed) - return; - - // Save the current scroll position and restore it after the rebuild. - let firstIndex = tagsSelector.getIndexOfFirstVisibleRow(); - let selectedIndex = tagsSelector.selectedIndex; - let selectedTag = selectedIndex >= 0 ? tagsSelector.selectedItem.label - : null; - - while (tagsSelector.hasChildNodes()) - tagsSelector.removeChild(tagsSelector.lastChild); - - var tagsInField = this._getTagsArrayFromTagField(); - var allTags = PlacesUtils.tagging.allTags; - for (var i = 0; i < allTags.length; i++) { - var tag = allTags[i]; - var elt = document.createElement("listitem"); - elt.setAttribute("type", "checkbox"); - elt.setAttribute("label", tag); - if (tagsInField.indexOf(tag) != -1) - elt.setAttribute("checked", "true"); - tagsSelector.appendChild(elt); - if (selectedTag === tag) - selectedIndex = tagsSelector.getIndexOfItem(elt); - } - - // Restore position. - // The listbox allows to scroll only if the required offset doesn't - // overflow its capacity, thus need to adjust the index for removals. - firstIndex = - Math.min(firstIndex, - tagsSelector.itemCount - tagsSelector.getNumberOfVisibleRows()); - tagsSelector.scrollToIndex(firstIndex); - if (selectedIndex >= 0 && tagsSelector.itemCount > 0) { - selectedIndex = Math.min(selectedIndex, tagsSelector.itemCount - 1); - tagsSelector.selectedIndex = selectedIndex; - tagsSelector.ensureIndexIsVisible(selectedIndex); - } - }, - - toggleTagsSelector: function EIO_toggleTagsSelector() { - var tagsSelector = this._element("tagsSelector"); - var tagsSelectorRow = this._element("tagsSelectorRow"); - var expander = this._element("tagsSelectorExpander"); - if (tagsSelectorRow.collapsed) { - expander.className = "expander-up"; - expander.setAttribute("tooltiptext", - expander.getAttribute("tooltiptextup")); - tagsSelectorRow.collapsed = false; - this._rebuildTagsSelectorList(); - - // This is a no-op if we've added the listener. - tagsSelector.addEventListener("CheckboxStateChange", this, false); - } - else { - expander.className = "expander-down"; - expander.setAttribute("tooltiptext", - expander.getAttribute("tooltiptextdown")); - tagsSelectorRow.collapsed = true; - } - }, - - /** - * Splits "tagsField" element value, returning an array of valid tag strings. - * - * @return Array of tag strings found in the field value. - */ - _getTagsArrayFromTagField: function EIO__getTagsArrayFromTagField() { - let tags = this._element("tagsField").value; - return tags.trim() - .split(/\s*,\s*/) // Split on commas and remove spaces. - .filter(function (tag) tag.length > 0); // Kill empty tags. - }, - - newFolder: function EIO_newFolder() { - var ip = this._folderTree.insertionPoint; - - // default to the bookmarks menu folder - if (!ip || ip.itemId == PlacesUIUtils.allBookmarksFolderId) { - ip = new InsertionPoint(PlacesUtils.bookmarksMenuFolderId, - PlacesUtils.bookmarks.DEFAULT_INDEX, - Ci.nsITreeView.DROP_ON); - } - - // XXXmano: add a separate "New Folder" string at some point... - var defaultLabel = this._element("newFolderButton").label; - var txn = new PlacesCreateFolderTransaction(defaultLabel, ip.itemId, ip.index); - PlacesUtils.transactionManager.doTransaction(txn); - this._folderTree.focus(); - this._folderTree.selectItems([this._lastNewItem]); - this._folderTree.startEditing(this._folderTree.view.selection.currentIndex, - this._folderTree.columns.getFirstColumn()); - }, - - // nsIDOMEventListener - handleEvent: function EIO_nsIDOMEventListener(aEvent) { - switch (aEvent.type) { - case "CheckboxStateChange": - // Update the tags field when items are checked/unchecked in the listbox - var tags = this._getTagsArrayFromTagField(); - - if (aEvent.target.checked) { - if (tags.indexOf(aEvent.target.label) == -1) - tags.push(aEvent.target.label); - } - else { - var indexOfItem = tags.indexOf(aEvent.target.label); - if (indexOfItem != -1) - tags.splice(indexOfItem, 1); - } - this._element("tagsField").value = tags.join(", "); - this._updateTags(); - break; - case "blur": - let replaceFn = (str, firstLetter) => firstLetter.toUpperCase(); - let nodeName = aEvent.target.id.replace(/editBMPanel_(\w)/, replaceFn); - this["on" + nodeName + "Blur"](); - break; - case "unload": - this.uninitPanel(false); - break; - } - }, - - // nsINavBookmarkObserver - onItemChanged: function EIO_onItemChanged(aItemId, aProperty, - aIsAnnotationProperty, aValue, - aLastModified, aItemType) { - if (aProperty == "tags") { - // Tags case is special, since they should be updated if either: - // - the notification is for the edited bookmark - // - the notification is for the edited history entry - // - the notification is for one of edited uris - let shouldUpdateTagsField = this._itemId == aItemId; - if (this._itemId == -1 || this._multiEdit) { - // Check if the changed uri is part of the modified ones. - let changedURI = PlacesUtils.bookmarks.getBookmarkURI(aItemId); - let uris = this._multiEdit ? this._uris : [this._uri]; - uris.forEach(function (aURI, aIndex) { - if (aURI.equals(changedURI)) { - shouldUpdateTagsField = true; - if (this._multiEdit) { - this._tags[aIndex] = PlacesUtils.tagging.getTagsForURI(this._uris[aIndex]); - } - } - }, this); - } - - if (shouldUpdateTagsField) { - if (this._multiEdit) { - this._allTags = this._getCommonTags(); - this._initTextField("tagsField", this._allTags.join(", "), false); - } - else { - let tags = PlacesUtils.tagging.getTagsForURI(this._uri).join(", "); - this._initTextField("tagsField", tags, false); - } - } - - // Any tags change should be reflected in the tags selector. - this._rebuildTagsSelectorList(); - return; - } - - if (this._itemId != aItemId) { - if (aProperty == "title") { - // If the title of a folder which is listed within the folders - // menulist has been changed, we need to update the label of its - // representing element. - var menupopup = this._folderMenuList.menupopup; - for (let i = 0; i < menupopup.childNodes.length; i++) { - if ("folderId" in menupopup.childNodes[i] && - menupopup.childNodes[i].folderId == aItemId) { - menupopup.childNodes[i].label = aValue; - break; - } - } - } - - return; - } - - switch (aProperty) { - case "title": - var namePicker = this._element("namePicker"); - if (namePicker.value != aValue) { - namePicker.value = aValue; - this._editorTransactionManagerClear(namePicker); - } - break; - case "uri": - var locationField = this._element("locationField"); - if (locationField.value != aValue) { - this._uri = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService). - newURI(aValue, null, null); - this._initTextField("locationField", this._uri.spec); - this._initNamePicker(); - this._initTextField("tagsField", - PlacesUtils.tagging - .getTagsForURI(this._uri).join(", "), - false); - this._rebuildTagsSelectorList(); - } - break; - case "keyword": - this._keyword = PlacesUtils.bookmarks.getKeywordForBookmark(this._itemId); - this._initTextField("keywordField", this._keyword); - break; - case PlacesUIUtils.DESCRIPTION_ANNO: - this._initTextField("descriptionField", - PlacesUIUtils.getItemDescription(this._itemId)); - break; - case PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO: - this._element("loadInSidebarCheckbox").checked = - PlacesUtils.annotations.itemHasAnnotation(this._itemId, - PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO); - break; - case PlacesUtils.LMANNO_FEEDURI: - let feedURISpec = - PlacesUtils.annotations.getItemAnnotation(this._itemId, - PlacesUtils.LMANNO_FEEDURI); - this._initTextField("feedLocationField", feedURISpec, true); - break; - case PlacesUtils.LMANNO_SITEURI: - let siteURISpec = ""; - try { - siteURISpec = - PlacesUtils.annotations.getItemAnnotation(this._itemId, - PlacesUtils.LMANNO_SITEURI); - } catch (ex) {} - this._initTextField("siteLocationField", siteURISpec, true); - break; - } - }, - - onItemMoved: function EIO_onItemMoved(aItemId, aOldParent, aOldIndex, - aNewParent, aNewIndex, aItemType) { - if (aItemId != this._itemId || - aNewParent == this._getFolderIdFromMenuList()) - return; - - var folderItem = this._getFolderMenuItem(aNewParent); - - // just setting selectItem _does not_ trigger oncommand, so we don't - // recurse - this._folderMenuList.selectedItem = folderItem; - }, - - onItemAdded: function EIO_onItemAdded(aItemId, aParentId, aIndex, aItemType, - aURI) { - this._lastNewItem = aItemId; - }, - - onItemRemoved: function() { }, - onBeginUpdateBatch: function() { }, - onEndUpdateBatch: function() { }, - onItemVisited: function() { }, -}; diff --git a/components/places/content/editBookmarkOverlay.xul b/components/places/content/editBookmarkOverlay.xul deleted file mode 100644 index 196369d..0000000 --- a/components/places/content/editBookmarkOverlay.xul +++ /dev/null @@ -1,228 +0,0 @@ -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<!DOCTYPE overlay [ -<!ENTITY % editBookmarkOverlayDTD SYSTEM "chrome://browser/locale/places/editBookmarkOverlay.dtd"> -%editBookmarkOverlayDTD; -]> - -<?xml-stylesheet href="chrome://browser/skin/places/editBookmarkOverlay.css"?> -<?xml-stylesheet href="chrome://browser/skin/places/places.css"?> - -<overlay id="editBookmarkOverlay" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - - <vbox id="editBookmarkPanelContent" flex="1"> - <broadcaster id="paneElementsBroadcaster"/> - - <hbox id="editBMPanel_selectionCount" hidden="true" pack="center"> - <label id="editBMPanel_itemsCountText"/> - </hbox> - - <grid id="editBookmarkPanelGrid" flex="1"> - <columns id="editBMPanel_columns"> - <column id="editBMPanel_labelColumn" /> - <column flex="1" id="editBMPanel_editColumn" /> - </columns> - <rows id="editBMPanel_rows"> - <row id="editBMPanel_nameRow" - align="center" - collapsed="true"> - <label value="&editBookmarkOverlay.name.label;" - class="editBMPanel_rowLabel" - accesskey="&editBookmarkOverlay.name.accesskey;" - control="editBMPanel_namePicker" - observes="paneElementsBroadcaster"/> - <textbox id="editBMPanel_namePicker" - observes="paneElementsBroadcaster"/> - </row> - - <row id="editBMPanel_locationRow" - align="center" - collapsed="true"> - <label value="&editBookmarkOverlay.location.label;" - class="editBMPanel_rowLabel" - accesskey="&editBookmarkOverlay.location.accesskey;" - control="editBMPanel_locationField" - observes="paneElementsBroadcaster"/> - <textbox id="editBMPanel_locationField" - class="uri-element" - observes="paneElementsBroadcaster"/> - </row> - - <row id="editBMPanel_feedLocationRow" - align="center" - collapsed="true"> - <label value="&editBookmarkOverlay.feedLocation.label;" - class="editBMPanel_rowLabel" - accesskey="&editBookmarkOverlay.feedLocation.accesskey;" - control="editBMPanel_feedLocationField" - observes="paneElementsBroadcaster"/> - <textbox id="editBMPanel_feedLocationField" - class="uri-element" - observes="paneElementsBroadcaster"/> - </row> - - <row id="editBMPanel_siteLocationRow" - align="center" - collapsed="true"> - <label value="&editBookmarkOverlay.siteLocation.label;" - class="editBMPanel_rowLabel" - accesskey="&editBookmarkOverlay.siteLocation.accesskey;" - control="editBMPanel_siteLocationField" - observes="paneElementsBroadcaster"/> - <textbox id="editBMPanel_siteLocationField" - class="uri-element" - observes="paneElementsBroadcaster"/> - </row> - - <row id="editBMPanel_folderRow" - align="center" - collapsed="true"> - <label value="&editBookmarkOverlay.folder.label;" - class="editBMPanel_rowLabel" - control="editBMPanel_folderMenuList" - observes="paneElementsBroadcaster"/> - <hbox flex="1" align="center"> - <menulist id="editBMPanel_folderMenuList" - class="folder-icon" - flex="1" - oncommand="gEditItemOverlay.onFolderMenuListCommand(event);" - observes="paneElementsBroadcaster"> - <menupopup> - <!-- Static item for special folders --> - <menuitem id="editBMPanel_toolbarFolderItem" - class="menuitem-iconic folder-icon"/> - <menuitem id="editBMPanel_bmRootItem" - class="menuitem-iconic folder-icon"/> - <menuitem id="editBMPanel_unfiledRootItem" - class="menuitem-iconic folder-icon"/> - <menuseparator id="editBMPanel_chooseFolderSeparator"/> - <menuitem id="editBMPanel_chooseFolderMenuItem" - label="&editBookmarkOverlay.choose.label;" - class="menuitem-iconic folder-icon"/> - <menuseparator id="editBMPanel_foldersSeparator" hidden="true"/> - </menupopup> - </menulist> - <button id="editBMPanel_foldersExpander" - class="expander-down" - tooltiptext="&editBookmarkOverlay.foldersExpanderDown.tooltip;" - tooltiptextdown="&editBookmarkOverlay.foldersExpanderDown.tooltip;" - tooltiptextup="&editBookmarkOverlay.expanderUp.tooltip;" - oncommand="gEditItemOverlay.toggleFolderTreeVisibility();" - observes="paneElementsBroadcaster"/> - </hbox> - </row> - - <row id="editBMPanel_folderTreeRow" - collapsed="true" - flex="1"> - <spacer/> - <vbox flex="1"> - <tree id="editBMPanel_folderTree" - flex="1" - class="placesTree" - type="places" - height="150" - minheight="150" - editable="true" - onselect="gEditItemOverlay.onFolderTreeSelect();" - hidecolumnpicker="true" - observes="paneElementsBroadcaster"> - <treecols> - <treecol anonid="title" flex="1" primary="true" hideheader="true"/> - </treecols> - <treechildren flex="1"/> - </tree> - - <hbox id="editBMPanel_newFolderBox"> - <button label="&editBookmarkOverlay.newFolderButton.label;" - id="editBMPanel_newFolderButton" - accesskey="&editBookmarkOverlay.newFolderButton.accesskey;" - oncommand="gEditItemOverlay.newFolder();"/> - </hbox> - </vbox> - </row> - - <row id="editBMPanel_tagsRow" - align="center" - collapsed="true"> - <label value="&editBookmarkOverlay.tags.label;" - class="editBMPanel_rowLabel" - accesskey="&editBookmarkOverlay.tags.accesskey;" - control="editBMPanel_tagsField" - observes="paneElementsBroadcaster"/> - <hbox flex="1" align="center"> - <textbox id="editBMPanel_tagsField" - type="autocomplete" - class="padded" - flex="1" - autocompletesearch="places-tag-autocomplete" - completedefaultindex="true" - tabscrolling="true" - showcommentcolumn="true" - observes="paneElementsBroadcaster" - placeholder="&editBookmarkOverlay.tagsEmptyDesc.label;"/> - <button id="editBMPanel_tagsSelectorExpander" - class="expander-down" - tooltiptext="&editBookmarkOverlay.tagsExpanderDown.tooltip;" - tooltiptextdown="&editBookmarkOverlay.tagsExpanderDown.tooltip;" - tooltiptextup="&editBookmarkOverlay.expanderUp.tooltip;" - oncommand="gEditItemOverlay.toggleTagsSelector();" - observes="paneElementsBroadcaster"/> - </hbox> - </row> - - <row id="editBMPanel_tagsSelectorRow" - align="center" - collapsed="true"> - <spacer/> - <listbox id="editBMPanel_tagsSelector" - height="150" - observes="paneElementsBroadcaster"/> - </row> - - <row id="editBMPanel_keywordRow" - align="center" - collapsed="true"> - <observes element="additionalInfoBroadcaster" attribute="hidden"/> - <label value="&editBookmarkOverlay.keyword.label;" - class="editBMPanel_rowLabel" - accesskey="&editBookmarkOverlay.keyword.accesskey;" - control="editBMPanel_keywordField" - observes="paneElementsBroadcaster"/> - <textbox id="editBMPanel_keywordField" - observes="paneElementsBroadcaster"/> - </row> - - <row id="editBMPanel_descriptionRow" - collapsed="true"> - <observes element="additionalInfoBroadcaster" attribute="hidden"/> - <label value="&editBookmarkOverlay.description.label;" - class="editBMPanel_rowLabel" - accesskey="&editBookmarkOverlay.description.accesskey;" - control="editBMPanel_descriptionField" - observes="paneElementsBroadcaster"/> - <textbox id="editBMPanel_descriptionField" - multiline="true" - observes="paneElementsBroadcaster"/> - </row> - </rows> - </grid> - - <checkbox id="editBMPanel_loadInSidebarCheckbox" - collapsed="true" - label="&editBookmarkOverlay.loadInSidebar.label;" - accesskey="&editBookmarkOverlay.loadInSidebar.accesskey;" - oncommand="gEditItemOverlay.onLoadInSidebarCheckboxCommand();" - observes="paneElementsBroadcaster"> - <observes element="additionalInfoBroadcaster" attribute="hidden"/> - </checkbox> - - <!-- If the ids are changing or additional fields are being added, be sure - to sync the values in places.js --> - <broadcaster id="additionalInfoBroadcaster"/> - - </vbox> -</overlay> diff --git a/components/places/content/history-panel.js b/components/places/content/history-panel.js deleted file mode 100644 index cda39dd..0000000 --- a/components/places/content/history-panel.js +++ /dev/null @@ -1,91 +0,0 @@ -/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var gHistoryTree; -var gSearchBox; -var gHistoryGrouping = ""; -var gSearching = false; - -function HistorySidebarInit() -{ - gHistoryTree = document.getElementById("historyTree"); - gSearchBox = document.getElementById("search-box"); - - gHistoryGrouping = document.getElementById("viewButton"). - getAttribute("selectedsort"); - - if (gHistoryGrouping == "site") - document.getElementById("bysite").setAttribute("checked", "true"); - else if (gHistoryGrouping == "visited") - document.getElementById("byvisited").setAttribute("checked", "true"); - else if (gHistoryGrouping == "lastvisited") - document.getElementById("bylastvisited").setAttribute("checked", "true"); - else if (gHistoryGrouping == "dayandsite") - document.getElementById("bydayandsite").setAttribute("checked", "true"); - else - document.getElementById("byday").setAttribute("checked", "true"); - - searchHistory(""); -} - -function GroupBy(groupingType) -{ - gHistoryGrouping = groupingType; - searchHistory(gSearchBox.value); -} - -function searchHistory(aInput) -{ - var query = PlacesUtils.history.getNewQuery(); - var options = PlacesUtils.history.getNewQueryOptions(); - - const NHQO = Ci.nsINavHistoryQueryOptions; - var sortingMode; - var resultType; - - switch (gHistoryGrouping) { - case "visited": - resultType = NHQO.RESULTS_AS_URI; - sortingMode = NHQO.SORT_BY_VISITCOUNT_DESCENDING; - break; - case "lastvisited": - resultType = NHQO.RESULTS_AS_URI; - sortingMode = NHQO.SORT_BY_DATE_DESCENDING; - break; - case "dayandsite": - resultType = NHQO.RESULTS_AS_DATE_SITE_QUERY; - break; - case "site": - resultType = NHQO.RESULTS_AS_SITE_QUERY; - sortingMode = NHQO.SORT_BY_TITLE_ASCENDING; - break; - case "day": - default: - resultType = NHQO.RESULTS_AS_DATE_QUERY; - break; - } - - if (aInput) { - query.searchTerms = aInput; - if (gHistoryGrouping != "visited" && gHistoryGrouping != "lastvisited") { - sortingMode = NHQO.SORT_BY_FRECENCY_DESCENDING; - resultType = NHQO.RESULTS_AS_URI; - } - } - - options.sortingMode = sortingMode; - options.resultType = resultType; - options.includeHidden = !!aInput; - - // call load() on the tree manually - // instead of setting the place attribute in history-panel.xul - // otherwise, we will end up calling load() twice - gHistoryTree.load([query], options); -} - -window.addEventListener("SidebarFocused", - function() - gSearchBox.focus(), - false); diff --git a/components/places/content/history-panel.xul b/components/places/content/history-panel.xul deleted file mode 100644 index d1c875a..0000000 --- a/components/places/content/history-panel.xul +++ /dev/null @@ -1,95 +0,0 @@ -<?xml version="1.0"?> <!-- -*- Mode: xml; indent-tabs-mode: nil; -*- --> - -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -<?xml-stylesheet href="chrome://browser/content/places/places.css"?> -<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> -<?xml-stylesheet href="chrome://browser/skin/places/places.css"?> - -<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?> -<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?> - -<!DOCTYPE page [ -<!ENTITY % placesDTD SYSTEM "chrome://browser/locale/places/places.dtd"> -%placesDTD; -]> - -<!-- we need to keep id="history-panel" for upgrade and switching - between versions of the browser --> - -<page id="history-panel" orient="vertical" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - onload="HistorySidebarInit();" - onunload="SidebarUtils.setMouseoverURL('');"> - - <script type="application/javascript" - src="chrome://browser/content/bookmarks/sidebarUtils.js"/> - <script type="application/javascript" - src="chrome://browser/content/places/history-panel.js"/> - - <commandset id="editMenuCommands"/> - <commandset id="placesCommands"/> - - <keyset id="editMenuKeys"> -#ifdef XP_MACOSX - <key id="key_delete2" keycode="VK_BACK" command="cmd_delete"/> -#endif - </keyset> - - <!-- required to overlay the context menu --> - <menupopup id="placesContext"/> - - <!-- Bookmarks and history tooltip --> - <tooltip id="bhTooltip"/> - - <hbox id="sidebar-search-container" align="center"> - <label id="sidebar-search-label" - value="&find.label;" accesskey="&find.accesskey;" - control="search-box"/> - <textbox id="search-box" flex="1" type="search" class="compact" - aria-controls="historyTree" - oncommand="searchHistory(this.value);"/> - <button id="viewButton" style="min-width:0px !important;" type="menu" - label="&view.label;" accesskey="&view.accesskey;" selectedsort="day" - persist="selectedsort"> - <menupopup> - <menuitem id="bydayandsite" label="&byDayAndSite.label;" - accesskey="&byDayAndSite.accesskey;" type="radio" - oncommand="this.parentNode.parentNode.setAttribute('selectedsort', 'dayandsite'); GroupBy('dayandsite');"/> - <menuitem id="bysite" label="&bySite.label;" - accesskey="&bySite.accesskey;" type="radio" - oncommand="this.parentNode.parentNode.setAttribute('selectedsort', 'site'); GroupBy('site');"/> - <menuitem id="byday" label="&byDate.label;" - accesskey="&byDate.accesskey;" - type="radio" - oncommand="this.parentNode.parentNode.setAttribute('selectedsort', 'day'); GroupBy('day');"/> - <menuitem id="byvisited" label="&byMostVisited.label;" - accesskey="&byMostVisited.accesskey;" - type="radio" - oncommand="this.parentNode.parentNode.setAttribute('selectedsort', 'visited'); GroupBy('visited');"/> - <menuitem id="bylastvisited" label="&byLastVisited.label;" - accesskey="&byLastVisited.accesskey;" - type="radio" - oncommand="this.parentNode.parentNode.setAttribute('selectedsort', 'lastvisited'); GroupBy('lastvisited');"/> - </menupopup> - </button> - </hbox> - - <tree id="historyTree" - class="sidebar-placesTree" - flex="1" - type="places" - context="placesContext" - hidecolumnpicker="true" - onkeypress="SidebarUtils.handleTreeKeyPress(event);" - onclick="SidebarUtils.handleTreeClick(this, event, true);" - onmousemove="SidebarUtils.handleTreeMouseMove(event);" - onmouseout="SidebarUtils.setMouseoverURL('');"> - <treecols> - <treecol id="title" flex="1" primary="true" hideheader="true"/> - </treecols> - <treechildren class="sidebar-placesTreechildren" flex="1" tooltip="bhTooltip"/> - </tree> -</page> diff --git a/components/places/content/menu.xml b/components/places/content/menu.xml deleted file mode 100644 index d4041ec..0000000 --- a/components/places/content/menu.xml +++ /dev/null @@ -1,488 +0,0 @@ -<?xml version="1.0"?> - -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -<bindings id="placesMenuBindings" - xmlns="http://www.mozilla.org/xbl" - xmlns:xbl="http://www.mozilla.org/xbl" - xmlns:html="http://www.w3.org/1999/xhtml" - xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - - <binding id="places-popup-base" - extends="chrome://global/content/bindings/popup.xml#popup"> - <content> - <xul:hbox flex="1"> - <xul:vbox class="menupopup-drop-indicator-bar" hidden="true"> - <xul:image class="menupopup-drop-indicator" mousethrough="always"/> - </xul:vbox> - <xul:arrowscrollbox class="popup-internal-box" flex="1" orient="vertical" - smoothscroll="false"> - <children/> - </xul:arrowscrollbox> - </xul:hbox> - </content> - - <implementation> - - <field name="_indicatorBar"> - document.getAnonymousElementByAttribute(this, "class", - "menupopup-drop-indicator-bar"); - </field> - - <field name="_scrollBox"> - document.getAnonymousElementByAttribute(this, "class", - "popup-internal-box"); - </field> - - <!-- This is the view that manage the popup --> - <field name="_rootView">PlacesUIUtils.getViewForNode(this);</field> - - <!-- Check if we should hide the drop indicator for the target --> - <method name="_hideDropIndicator"> - <parameter name="aEvent"/> - <body><![CDATA[ - let target = aEvent.target; - - // Don't draw the drop indicator outside of markers. - // The markers are hidden, since otherwise sometimes popups acquire - // scrollboxes on OS X, so we can't use them directly. - let firstChildTop = this._startMarker.nextSibling.boxObject.y; - let lastChildBottom = this._endMarker.previousSibling.boxObject.y + - this._endMarker.previousSibling.boxObject.height; - let betweenMarkers = target.boxObject.y >= firstChildTop || - target.boxObject.y <= lastChildBottom; - - // Hide the dropmarker if current node is not a Places node. - return !(target && target._placesNode && betweenMarkers); - ]]></body> - </method> - - <!-- This function returns information about where to drop when - dragging over this popup insertion point --> - <method name="_getDropPoint"> - <parameter name="aEvent"/> - <body><![CDATA[ - // Can't drop if the menu isn't a folder - let resultNode = this._placesNode; - - if (!PlacesUtils.nodeIsFolder(resultNode) || - PlacesControllerDragHelper.disallowInsertion(resultNode)) { - return null; - } - - var dropPoint = { ip: null, folderElt: null }; - - // The element we are dragging over - let elt = aEvent.target; - if (elt.localName == "menupopup") - elt = elt.parentNode; - - // Calculate positions taking care of arrowscrollbox - let eventY = aEvent.layerY; - let scrollbox = this._scrollBox; - let scrollboxOffset = scrollbox.scrollBoxObject.y - - (scrollbox.boxObject.y - this.boxObject.y); - let eltY = elt.boxObject.y - scrollboxOffset; - let eltHeight = elt.boxObject.height; - - if (!elt._placesNode) { - // If we are dragging over a non places node drop at the end. - dropPoint.ip = new InsertionPoint( - PlacesUtils.getConcreteItemId(resultNode), - -1, - Ci.nsITreeView.DROP_ON); - // We can set folderElt if we are dropping over a static menu that - // has an internal placespopup. - let isMenu = elt.localName == "menu" || - (elt.localName == "toolbarbutton" && - elt.getAttribute("type") == "menu"); - if (isMenu && elt.lastChild && - elt.lastChild.hasAttribute("placespopup")) - dropPoint.folderElt = elt; - return dropPoint; - } - if ((PlacesUtils.nodeIsFolder(elt._placesNode) && - !PlacesUIUtils.isContentsReadOnly(elt._placesNode)) || - PlacesUtils.nodeIsTagQuery(elt._placesNode)) { - // This is a folder or a tag container. - if (eventY - eltY < eltHeight * 0.20) { - // If mouse is in the top part of the element, drop above folder. - dropPoint.ip = new InsertionPoint( - PlacesUtils.getConcreteItemId(resultNode), - -1, - Ci.nsITreeView.DROP_BEFORE, - PlacesUtils.nodeIsTagQuery(elt._placesNode), - elt._placesNode.itemId); - return dropPoint; - } - else if (eventY - eltY < eltHeight * 0.80) { - // If mouse is in the middle of the element, drop inside folder. - dropPoint.ip = new InsertionPoint( - PlacesUtils.getConcreteItemId(elt._placesNode), - -1, - Ci.nsITreeView.DROP_ON, - PlacesUtils.nodeIsTagQuery(elt._placesNode)); - dropPoint.folderElt = elt; - return dropPoint; - } - } - else if (eventY - eltY <= eltHeight / 2) { - // This is a non-folder node or a readonly folder. - // If the mouse is above the middle, drop above this item. - dropPoint.ip = new InsertionPoint( - PlacesUtils.getConcreteItemId(resultNode), - -1, - Ci.nsITreeView.DROP_BEFORE, - PlacesUtils.nodeIsTagQuery(elt._placesNode), - elt._placesNode.itemId); - return dropPoint; - } - - // Drop below the item. - dropPoint.ip = new InsertionPoint( - PlacesUtils.getConcreteItemId(resultNode), - -1, - Ci.nsITreeView.DROP_AFTER, - PlacesUtils.nodeIsTagQuery(elt._placesNode), - elt._placesNode.itemId); - return dropPoint; - ]]></body> - </method> - - <!-- Sub-menus should be opened when the mouse drags over them, and closed - when the mouse drags off. The overFolder object manages opening and - closing of folders when the mouse hovers. --> - <field name="_overFolder"><![CDATA[({ - _self: this, - _folder: {elt: null, - openTimer: null, - hoverTime: 350, - closeTimer: null}, - _closeMenuTimer: null, - - get elt() { - return this._folder.elt; - }, - set elt(val) { - return this._folder.elt = val; - }, - - get openTimer() { - return this._folder.openTimer; - }, - set openTimer(val) { - return this._folder.openTimer = val; - }, - - get hoverTime() { - return this._folder.hoverTime; - }, - set hoverTime(val) { - return this._folder.hoverTime = val; - }, - - get closeTimer() { - return this._folder.closeTimer; - }, - set closeTimer(val) { - return this._folder.closeTimer = val; - }, - - get closeMenuTimer() { - return this._closeMenuTimer; - }, - set closeMenuTimer(val) { - return this._closeMenuTimer = val; - }, - - setTimer: function OF__setTimer(aTime) { - var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - timer.initWithCallback(this, aTime, timer.TYPE_ONE_SHOT); - return timer; - }, - - notify: function OF__notify(aTimer) { - // Function to process all timer notifications. - - if (aTimer == this._folder.openTimer) { - // Timer to open a submenu that's being dragged over. - this._folder.elt.lastChild.setAttribute("autoopened", "true"); - this._folder.elt.lastChild.showPopup(this._folder.elt); - this._folder.openTimer = null; - } - - else if (aTimer == this._folder.closeTimer) { - // Timer to close a submenu that's been dragged off of. - // Only close the submenu if the mouse isn't being dragged over any - // of its child menus. - var draggingOverChild = PlacesControllerDragHelper - .draggingOverChildNode(this._folder.elt); - if (draggingOverChild) - this._folder.elt = null; - this.clear(); - - // Close any parent folders which aren't being dragged over. - // (This is necessary because of the above code that keeps a folder - // open while its children are being dragged over.) - if (!draggingOverChild) - this.closeParentMenus(); - } - - else if (aTimer == this.closeMenuTimer) { - // Timer to close this menu after the drag exit. - var popup = this._self; - // if we are no more dragging we can leave the menu open to allow - // for better D&D bookmark organization - if (PlacesControllerDragHelper.getSession() && - !PlacesControllerDragHelper.draggingOverChildNode(popup.parentNode)) { - popup.hidePopup(); - // Close any parent menus that aren't being dragged over; - // otherwise they'll stay open because they couldn't close - // while this menu was being dragged over. - this.closeParentMenus(); - } - this._closeMenuTimer = null; - } - }, - - // Helper function to close all parent menus of this menu, - // as long as none of the parent's children are currently being - // dragged over. - closeParentMenus: function OF__closeParentMenus() { - var popup = this._self; - var parent = popup.parentNode; - while (parent) { - if (parent.localName == "menupopup" && parent._placesNode) { - if (PlacesControllerDragHelper.draggingOverChildNode(parent.parentNode)) - break; - parent.hidePopup(); - } - parent = parent.parentNode; - } - }, - - // The mouse is no longer dragging over the stored menubutton. - // Close the menubutton, clear out drag styles, and clear all - // timers for opening/closing it. - clear: function OF__clear() { - if (this._folder.elt && this._folder.elt.lastChild) { - if (!this._folder.elt.lastChild.hasAttribute("dragover")) - this._folder.elt.lastChild.hidePopup(); - // remove menuactive style - this._folder.elt.removeAttribute("_moz-menuactive"); - this._folder.elt = null; - } - if (this._folder.openTimer) { - this._folder.openTimer.cancel(); - this._folder.openTimer = null; - } - if (this._folder.closeTimer) { - this._folder.closeTimer.cancel(); - this._folder.closeTimer = null; - } - } - })]]></field> - - <method name="_cleanupDragDetails"> - <body><![CDATA[ - // Called on dragend and drop. - PlacesControllerDragHelper.currentDropTarget = null; - this._rootView._draggedElt = null; - this.removeAttribute("dragover"); - this.removeAttribute("dragstart"); - this._indicatorBar.hidden = true; - ]]></body> - </method> - - </implementation> - - <handlers> - <handler event="DOMMenuItemActive"><![CDATA[ - let elt = event.target; - if (elt.parentNode != this) - return; - -#ifdef XP_MACOSX - // XXX: The following check is a temporary hack until bug 420033 is - // resolved. - let parentElt = elt.parent; - while (parentElt) { - if (parentElt.id == "bookmarksMenuPopup" || - parentElt.id == "goPopup") - return; - - parentElt = parentElt.parentNode; - } -#endif - - if (window.XULBrowserWindow) { - let elt = event.target; - let placesNode = elt._placesNode; - - var linkURI; - if (placesNode && PlacesUtils.nodeIsURI(placesNode)) - linkURI = placesNode.uri; - else if (elt.hasAttribute("targetURI")) - linkURI = elt.getAttribute("targetURI"); - - if (linkURI) - window.XULBrowserWindow.setOverLink(linkURI, null); - } - ]]></handler> - - <handler event="DOMMenuItemInactive"><![CDATA[ - let elt = event.target; - if (elt.parentNode != this) - return; - - if (window.XULBrowserWindow) - window.XULBrowserWindow.setOverLink("", null); - ]]></handler> - - <handler event="dragstart"><![CDATA[ - if (!event.target._placesNode) - return; - - let draggedElt = event.target._placesNode; - - // Force a copy action if parent node is a query or we are dragging a - // not-removable node. - if (!PlacesControllerDragHelper.canMoveNode(draggedElt)) - event.dataTransfer.effectAllowed = "copyLink"; - - // Activate the view and cache the dragged element. - this._rootView._draggedElt = draggedElt; - this._rootView.controller.setDataTransfer(event); - this.setAttribute("dragstart", "true"); - event.stopPropagation(); - ]]></handler> - - <handler event="drop"><![CDATA[ - PlacesControllerDragHelper.currentDropTarget = event.target; - - let dropPoint = this._getDropPoint(event); - if (dropPoint && dropPoint.ip) { - PlacesControllerDragHelper.onDrop(dropPoint.ip, event.dataTransfer); - event.preventDefault(); - } - - this._cleanupDragDetails(); - event.stopPropagation(); - ]]></handler> - - <handler event="dragover"><![CDATA[ - PlacesControllerDragHelper.currentDropTarget = event.target; - let dt = event.dataTransfer; - - let dropPoint = this._getDropPoint(event); - if (!dropPoint || !dropPoint.ip || - !PlacesControllerDragHelper.canDrop(dropPoint.ip, dt)) { - this._indicatorBar.hidden = true; - event.stopPropagation(); - return; - } - - // Mark this popup as being dragged over. - this.setAttribute("dragover", "true"); - - if (dropPoint.folderElt) { - // We are dragging over a folder. - // _overFolder should take the care of opening it on a timer. - if (this._overFolder.elt && - this._overFolder.elt != dropPoint.folderElt) { - // We are dragging over a new folder, let's clear old values - this._overFolder.clear(); - } - if (!this._overFolder.elt) { - this._overFolder.elt = dropPoint.folderElt; - // Create the timer to open this folder. - this._overFolder.openTimer = this._overFolder - .setTimer(this._overFolder.hoverTime); - } - // Since we are dropping into a folder set the corresponding style. - dropPoint.folderElt.setAttribute("_moz-menuactive", true); - } - else { - // We are not dragging over a folder. - // Clear out old _overFolder information. - this._overFolder.clear(); - } - - // Autoscroll the popup strip if we drag over the scroll buttons. - let anonid = event.originalTarget.getAttribute('anonid'); - let scrollDir = anonid == "scrollbutton-up" ? -1 : - anonid == "scrollbutton-down" ? 1 : 0; - if (scrollDir != 0) { - this._scrollBox.scrollByIndex(scrollDir, false); - } - - // Check if we should hide the drop indicator for this target. - if (dropPoint.folderElt || this._hideDropIndicator(event)) { - this._indicatorBar.hidden = true; - event.preventDefault(); - event.stopPropagation(); - return; - } - - // We should display the drop indicator relative to the arrowscrollbox. - let sbo = this._scrollBox.scrollBoxObject; - let newMarginTop = 0; - if (scrollDir == 0) { - let elt = this.firstChild; - while (elt && event.screenY > elt.boxObject.screenY + - elt.boxObject.height / 2) - elt = elt.nextSibling; - newMarginTop = elt ? elt.boxObject.screenY - sbo.screenY : - sbo.height; - } - else if (scrollDir == 1) - newMarginTop = sbo.height; - - // Set the new marginTop based on arrowscrollbox. - newMarginTop += sbo.y - this._scrollBox.boxObject.y; - this._indicatorBar.firstChild.style.marginTop = newMarginTop + "px"; - this._indicatorBar.hidden = false; - - event.preventDefault(); - event.stopPropagation(); - ]]></handler> - - <handler event="dragexit"><![CDATA[ - PlacesControllerDragHelper.currentDropTarget = null; - this.removeAttribute("dragover"); - - // If we have not moved to a valid new target clear the drop indicator - // this happens when moving out of the popup. - let target = event.relatedTarget; - if (!target) - this._indicatorBar.hidden = true; - - // Close any folder being hovered over - if (this._overFolder.elt) { - this._overFolder.closeTimer = this._overFolder - .setTimer(this._overFolder.hoverTime); - } - - // The autoopened attribute is set when this folder was automatically - // opened after the user dragged over it. If this attribute is set, - // auto-close the folder on drag exit. - // We should also try to close this popup if the drag has started - // from here, the timer will check if we are dragging over a child. - if (this.hasAttribute("autoopened") || - this.hasAttribute("dragstart")) { - this._overFolder.closeMenuTimer = this._overFolder - .setTimer(this._overFolder.hoverTime); - } - - event.stopPropagation(); - ]]></handler> - - <handler event="dragend"><![CDATA[ - this._cleanupDragDetails(); - ]]></handler> - - </handlers> - </binding> -</bindings> diff --git a/components/places/content/moveBookmarks.js b/components/places/content/moveBookmarks.js deleted file mode 100644 index 964604f..0000000 --- a/components/places/content/moveBookmarks.js +++ /dev/null @@ -1,54 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var gMoveBookmarksDialog = { - _nodes: null, - - _foldersTree: null, - get foldersTree() { - if (!this._foldersTree) - this._foldersTree = document.getElementById("foldersTree"); - - return this._foldersTree; - }, - - init: function() { - this._nodes = window.arguments[0]; - - this.foldersTree.place = - "place:excludeItems=1&excludeQueries=1&excludeReadOnlyFolders=1&folder=" + - PlacesUIUtils.allBookmarksFolderId; - }, - - onOK: function MBD_onOK(aEvent) { - var selectedNode = this.foldersTree.selectedNode; - NS_ASSERT(selectedNode, - "selectedNode must be set in a single-selection tree with initial selection set"); - var selectedFolderID = PlacesUtils.getConcreteItemId(selectedNode); - - var transactions = []; - for (var i=0; i < this._nodes.length; i++) { - // Nothing to do if the node is already under the selected folder - if (this._nodes[i].parent.itemId == selectedFolderID) - continue; - - let txn = new PlacesMoveItemTransaction(this._nodes[i].itemId, - selectedFolderID, - PlacesUtils.bookmarks.DEFAULT_INDEX); - transactions.push(txn); - } - - if (transactions.length != 0) { - let txn = new PlacesAggregatedTransaction("Move Items", transactions); - PlacesUtils.transactionManager.doTransaction(txn); - } - }, - - newFolder: function MBD_newFolder() { - // The command is disabled when the tree is not focused - this.foldersTree.focus(); - goDoCommand("placesCmd_new:folder"); - } -}; diff --git a/components/places/content/moveBookmarks.xul b/components/places/content/moveBookmarks.xul deleted file mode 100644 index b6e75f3..0000000 --- a/components/places/content/moveBookmarks.xul +++ /dev/null @@ -1,53 +0,0 @@ -<?xml version="1.0"?> - -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<?xml-stylesheet href="chrome://global/skin/"?> -<?xml-stylesheet href="chrome://browser/skin/places/places.css"?> -<?xml-stylesheet href="chrome://browser/content/places/places.css"?> - -<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?> - -<!DOCTYPE window [ - <!ENTITY % moveBookmarksDTD SYSTEM "chrome://browser/locale/places/moveBookmarks.dtd"> - %moveBookmarksDTD; -]> - -<dialog id="moveBookmarkDialog" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - ondialogaccept="return gMoveBookmarksDialog.onOK(event);" - title="&window.title;" - onload="gMoveBookmarksDialog.init();" - style="&window.style;" - screenX="24" - screenY="24" - persist="screenX screenY width height"> - - <script type="application/javascript" - src="chrome://browser/content/places/moveBookmarks.js"/> - - <hbox flex="1"> - <label id="movetolabel" value="&moveTo.label;" control="foldersTree"/> - <hbox flex="1"> - <tree id="foldersTree" - class="placesTree" - flex="1" - type="places" - seltype="single" - hidecolumnpicker="true"> - <treecols> - <treecol id="title" flex="1" primary="true" hideheader="true"/> - </treecols> - <treechildren id="placesListChildren" view="placesList" flex="1"/> - </tree> - <vbox> - <button id="newFolderButton" - label="&newFolderButton.label;" - accesskey="&newFolderButton.accesskey;" - oncommand="gMoveBookmarksDialog.newFolder();"/> - </vbox> - </hbox> - </hbox> -</dialog> diff --git a/components/places/content/organizer.css b/components/places/content/organizer.css deleted file mode 100644 index 47b1832..0000000 --- a/components/places/content/organizer.css +++ /dev/null @@ -1,7 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#searchFilter { - width: 23em; -} diff --git a/components/places/content/places.css b/components/places/content/places.css deleted file mode 100644 index 5151cca..0000000 --- a/components/places/content/places.css +++ /dev/null @@ -1,16 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -tree[type="places"] { - -moz-binding: url("chrome://browser/content/places/tree.xml#places-tree"); -} - -.toolbar-drop-indicator { - position: relative; - z-index: 1; -} - -menupopup[placespopup="true"] { - -moz-binding: url("chrome://browser/content/places/menu.xml#places-popup-base"); -} diff --git a/components/places/content/places.js b/components/places/content/places.js deleted file mode 100644 index 40dbcb9..0000000 --- a/components/places/content/places.js +++ /dev/null @@ -1,1553 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Task", - "resource://gre/modules/Task.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "BookmarkJSONUtils", - "resource://gre/modules/BookmarkJSONUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PlacesBackups", - "resource://gre/modules/PlacesBackups.jsm"); - -const RESTORE_FILEPICKER_FILTER_EXT = "*.json;*.jsonlz4"; - -var PlacesOrganizer = { - _places: null, - - // IDs of fields from editBookmarkOverlay that should be hidden when infoBox - // is minimal. IDs should be kept in sync with the IDs of the elements - // observing additionalInfoBroadcaster. - _additionalInfoFields: [ - "editBMPanel_descriptionRow", - "editBMPanel_loadInSidebarCheckbox", - "editBMPanel_keywordRow", - ], - - _initFolderTree: function() { - var leftPaneRoot = PlacesUIUtils.leftPaneFolderId; - this._places.place = "place:excludeItems=1&expandQueries=0&folder=" + leftPaneRoot; - }, - - selectLeftPaneQuery: function PO_selectLeftPaneQuery(aQueryName) { - var itemId = PlacesUIUtils.leftPaneQueries[aQueryName]; - this._places.selectItems([itemId]); - // Forcefully expand all-bookmarks - if (aQueryName == "AllBookmarks" || aQueryName == "History") - PlacesUtils.asContainer(this._places.selectedNode).containerOpen = true; - }, - - init: function PO_init() { - ContentArea.init(); - - this._places = document.getElementById("placesList"); - this._initFolderTree(); - - var leftPaneSelection = "AllBookmarks"; // default to all-bookmarks - if (window.arguments && window.arguments[0]) - leftPaneSelection = window.arguments[0]; - - this.selectLeftPaneQuery(leftPaneSelection); - if (leftPaneSelection == "History") { - let historyNode = this._places.selectedNode; - if (historyNode.childCount > 0) - this._places.selectNode(historyNode.getChild(0)); - } - // clear the back-stack - this._backHistory.splice(0, this._backHistory.length); - document.getElementById("OrganizerCommand:Back").setAttribute("disabled", true); - - // Set up the search UI. - PlacesSearchBox.init(); - - window.addEventListener("AppCommand", this, true); -#ifdef XP_MACOSX - // 1. Map Edit->Find command to OrganizerCommand_find:all. Need to map - // both the menuitem and the Find key. - var findMenuItem = document.getElementById("menu_find"); - findMenuItem.setAttribute("command", "OrganizerCommand_find:all"); - var findKey = document.getElementById("key_find"); - findKey.setAttribute("command", "OrganizerCommand_find:all"); - - // 2. Disable some keybindings from browser.xul - var elements = ["cmd_handleBackspace", "cmd_handleShiftBackspace"]; - for (var i=0; i < elements.length; i++) { - document.getElementById(elements[i]).setAttribute("disabled", "true"); - } - - // 3. Disable the keyboard shortcut for the History menu back/forward - // in order to support those in the Library - var historyMenuBack = document.getElementById("historyMenuBack"); - historyMenuBack.removeAttribute("key"); - var historyMenuForward = document.getElementById("historyMenuForward"); - historyMenuForward.removeAttribute("key"); -#endif - - // remove the "Properties" context-menu item, we've our own details pane - document.getElementById("placesContext") - .removeChild(document.getElementById("placesContext_show:info")); - - ContentArea.focus(); - }, - - QueryInterface: function PO_QueryInterface(aIID) { - if (aIID.equals(Components.interfaces.nsIDOMEventListener) || - aIID.equals(Components.interfaces.nsISupports)) - return this; - - throw new Components.Exception("", Components.results.NS_NOINTERFACE); - }, - - handleEvent: function PO_handleEvent(aEvent) { - if (aEvent.type != "AppCommand") - return; - - aEvent.stopPropagation(); - switch (aEvent.command) { - case "Back": - if (this._backHistory.length > 0) - this.back(); - break; - case "Forward": - if (this._forwardHistory.length > 0) - this.forward(); - break; - case "Search": - PlacesSearchBox.findAll(); - break; - } - }, - - destroy: function PO_destroy() { - }, - - _location: null, - get location() { - return this._location; - }, - - set location(aLocation) { - if (!aLocation || this._location == aLocation) - return aLocation; - - if (this.location) { - this._backHistory.unshift(this.location); - this._forwardHistory.splice(0, this._forwardHistory.length); - } - - this._location = aLocation; - this._places.selectPlaceURI(aLocation); - - if (!this._places.hasSelection) { - // If no node was found for the given place: uri, just load it directly - ContentArea.currentPlace = aLocation; - } - this.updateDetailsPane(); - - // update navigation commands - if (this._backHistory.length == 0) - document.getElementById("OrganizerCommand:Back").setAttribute("disabled", true); - else - document.getElementById("OrganizerCommand:Back").removeAttribute("disabled"); - if (this._forwardHistory.length == 0) - document.getElementById("OrganizerCommand:Forward").setAttribute("disabled", true); - else - document.getElementById("OrganizerCommand:Forward").removeAttribute("disabled"); - - return aLocation; - }, - - _backHistory: [], - _forwardHistory: [], - - back: function PO_back() { - this._forwardHistory.unshift(this.location); - var historyEntry = this._backHistory.shift(); - this._location = null; - this.location = historyEntry; - }, - forward: function PO_forward() { - this._backHistory.unshift(this.location); - var historyEntry = this._forwardHistory.shift(); - this._location = null; - this.location = historyEntry; - }, - - /** - * Called when a place folder is selected in the left pane. - * @param resetSearchBox - * true if the search box should also be reset, false otherwise. - * The search box should be reset when a new folder in the left - * pane is selected; the search scope and text need to be cleared in - * preparation for the new folder. Note that if the user manually - * resets the search box, either by clicking its reset button or by - * deleting its text, this will be false. - */ - _cachedLeftPaneSelectedURI: null, - onPlaceSelected: function PO_onPlaceSelected(resetSearchBox) { - // Don't change the right-hand pane contents when there's no selection. - if (!this._places.hasSelection) - return; - - var node = this._places.selectedNode; - var queries = PlacesUtils.asQuery(node).getQueries(); - - // Items are only excluded on the left pane. - var options = node.queryOptions.clone(); - options.excludeItems = false; - var placeURI = PlacesUtils.history.queriesToQueryString(queries, - queries.length, - options); - - // If either the place of the content tree in the right pane has changed or - // the user cleared the search box, update the place, hide the search UI, - // and update the back/forward buttons by setting location. - if (ContentArea.currentPlace != placeURI || !resetSearchBox) { - ContentArea.currentPlace = placeURI; - PlacesSearchBox.hideSearchUI(); - this.location = node.uri; - } - - // Update the selected folder title where it appears in the UI: the folder - // scope button, and the search box emptytext. - // They must be updated even if the selection hasn't changed -- - // specifically when node's title changes. In that case a selection event - // is generated, this method is called, but the selection does not change. - var folderButton = document.getElementById("scopeBarFolder"); - var folderTitle = node.title || folderButton.getAttribute("emptytitle"); - folderButton.setAttribute("label", folderTitle); - if (PlacesSearchBox.filterCollection == "collection") - PlacesSearchBox.updateCollectionTitle(folderTitle); - - // When we invalidate a container we use suppressSelectionEvent, when it is - // unset a select event is fired, in many cases the selection did not really - // change, so we should check for it, and return early in such a case. Note - // that we cannot return any earlier than this point, because when - // !resetSearchBox, we need to update location and hide the UI as above, - // even though the selection has not changed. - if (node.uri == this._cachedLeftPaneSelectedURI) - return; - this._cachedLeftPaneSelectedURI = node.uri; - - // At this point, resetSearchBox is true, because the left pane selection - // has changed; otherwise we would have returned earlier. - - PlacesSearchBox.searchFilter.reset(); - this._setSearchScopeForNode(node); - this.updateDetailsPane(); - }, - - /** - * Sets the search scope based on aNode's properties. - * @param aNode - * the node to set up scope from - */ - _setSearchScopeForNode: function PO__setScopeForNode(aNode) { - let itemId = aNode.itemId; - - // Set default buttons status. - let bookmarksButton = document.getElementById("scopeBarAll"); - bookmarksButton.hidden = false; - let downloadsButton = document.getElementById("scopeBarDownloads"); - downloadsButton.hidden = true; - - if (PlacesUtils.nodeIsHistoryContainer(aNode) || - itemId == PlacesUIUtils.leftPaneQueries["History"]) { - PlacesQueryBuilder.setScope("history"); - } - else if (itemId == PlacesUIUtils.leftPaneQueries["Downloads"]) { - downloadsButton.hidden = false; - bookmarksButton.hidden = true; - PlacesQueryBuilder.setScope("downloads"); - } - else { - // Default to All Bookmarks for all other nodes, per bug 469437. - PlacesQueryBuilder.setScope("bookmarks"); - } - - // Enable or disable the folder scope button. - let folderButton = document.getElementById("scopeBarFolder"); - folderButton.hidden = !PlacesUtils.nodeIsFolder(aNode) || - itemId == PlacesUIUtils.allBookmarksFolderId; - }, - - /** - * Handle clicks on the places list. - * Single Left click, right click or modified click do not result in any - * special action, since they're related to selection. - * @param aEvent - * The mouse event. - */ - onPlacesListClick: function PO_onPlacesListClick(aEvent) { - // Only handle clicks on tree children. - if (aEvent.target.localName != "treechildren") - return; - - let node = this._places.selectedNode; - if (node) { - let middleClick = aEvent.button == 1 && aEvent.detail == 1; - if (middleClick && PlacesUtils.nodeIsContainer(node)) { - // The command execution function will take care of seeing if the - // selection is a folder or a different container type, and will - // load its contents in tabs. - PlacesUIUtils.openContainerNodeInTabs(selectedNode, aEvent, this._places); - } - } - }, - - /** - * Handle focus changes on the places list and the current content view. - */ - updateDetailsPane: function PO_updateDetailsPane() { - if (!ContentArea.currentViewOptions.showDetailsPane) - return; - let view = PlacesUIUtils.getViewForNode(document.activeElement); - if (view) { - let selectedNodes = view.selectedNode ? - [view.selectedNode] : view.selectedNodes; - this._fillDetailsPane(selectedNodes); - } - }, - - openFlatContainer: function PO_openFlatContainerFlatContainer(aContainer) { - if (aContainer.itemId != -1) - this._places.selectItems([aContainer.itemId]); - else if (PlacesUtils.nodeIsQuery(aContainer)) - this._places.selectPlaceURI(aContainer.uri); - }, - - /** - * Returns the options associated with the query currently loaded in the - * main places pane. - */ - getCurrentOptions: function PO_getCurrentOptions() { - return PlacesUtils.asQuery(ContentArea.currentView.result.root).queryOptions; - }, - - /** - * Returns the queries associated with the query currently loaded in the - * main places pane. - */ - getCurrentQueries: function PO_getCurrentQueries() { - return PlacesUtils.asQuery(ContentArea.currentView.result.root).getQueries(); - }, - - /** - * Open a file-picker and import the selected file into the bookmarks store - */ - importFromFile: function PO_importFromFile() { - let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker); - let fpCallback = function fpCallback_done(aResult) { - if (aResult != Ci.nsIFilePicker.returnCancel && fp.fileURL) { - Components.utils.import("resource://gre/modules/BookmarkHTMLUtils.jsm"); - BookmarkHTMLUtils.importFromURL(fp.fileURL.spec, false) - .then(null, Components.utils.reportError); - } - }; - - fp.init(window, PlacesUIUtils.getString("SelectImport"), - Ci.nsIFilePicker.modeOpen); - fp.appendFilters(Ci.nsIFilePicker.filterHTML); - fp.open(fpCallback); - }, - - /** - * Allows simple exporting of bookmarks. - */ - exportBookmarks: function PO_exportBookmarks() { - let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker); - let fpCallback = function fpCallback_done(aResult) { - if (aResult != Ci.nsIFilePicker.returnCancel) { - Components.utils.import("resource://gre/modules/BookmarkHTMLUtils.jsm"); - BookmarkHTMLUtils.exportToFile(fp.file.path) - .then(null, Components.utils.reportError); - } - }; - - fp.init(window, PlacesUIUtils.getString("EnterExport"), - Ci.nsIFilePicker.modeSave); - fp.appendFilters(Ci.nsIFilePicker.filterHTML); - fp.defaultString = "bookmarks.html"; - fp.open(fpCallback); - }, - - /** - * Populates the restore menu with the dates of the backups available. - */ - populateRestoreMenu: function PO_populateRestoreMenu() { - let restorePopup = document.getElementById("fileRestorePopup"); - - let dateSvc = Cc["@mozilla.org/intl/scriptabledateformat;1"]. - getService(Ci.nsIScriptableDateFormat); - - // Remove existing menu items. Last item is the restoreFromFile item. - while (restorePopup.childNodes.length > 1) - restorePopup.removeChild(restorePopup.firstChild); - - Task.spawn(function() { - let backupFiles = yield PlacesBackups.getBackupFiles(); - if (backupFiles.length == 0) - return; - - // Populate menu with backups. - for (let i = 0; i < backupFiles.length; i++) { - let fileSize = (yield OS.File.stat(backupFiles[i])).size; - let [size, unit] = DownloadUtils.convertByteUnits(fileSize); - let sizeString = PlacesUtils.getFormattedString("backupFileSizeText", - [size, unit]); - let sizeInfo; - let bookmarkCount = PlacesBackups.getBookmarkCountForFile(backupFiles[i]); - if (bookmarkCount != null) { - sizeInfo = " (" + sizeString + " - " + - PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel", - bookmarkCount, - [bookmarkCount]) + - ")"; - } else { - sizeInfo = " (" + sizeString + ")"; - } - - let backupDate = PlacesBackups.getDateForFile(backupFiles[i]); - let m = restorePopup.insertBefore(document.createElement("menuitem"), - document.getElementById("restoreFromFile")); - m.setAttribute("label", - dateSvc.FormatDate("", - Ci.nsIScriptableDateFormat.dateFormatLong, - backupDate.getFullYear(), - backupDate.getMonth() + 1, - backupDate.getDate()) + - sizeInfo); - m.setAttribute("value", OS.Path.basename(backupFiles[i])); - m.setAttribute("oncommand", - "PlacesOrganizer.onRestoreMenuItemClick(this);"); - } - - // Add the restoreFromFile item. - restorePopup.insertBefore(document.createElement("menuseparator"), - document.getElementById("restoreFromFile")); - }); - }, - - /** - * Called when a menuitem is selected from the restore menu. - */ - onRestoreMenuItemClick: function PO_onRestoreMenuItemClick(aMenuItem) { - Task.spawn(function() { - let backupName = aMenuItem.getAttribute("value"); - let backupFilePaths = yield PlacesBackups.getBackupFiles(); - for (let backupFilePath of backupFilePaths) { - if (OS.Path.basename(backupFilePath) == backupName) { - PlacesOrganizer.restoreBookmarksFromFile(new FileUtils.File(backupFilePath)); - break; - } - } - }); - }, - - /** - * Called when 'Choose File...' is selected from the restore menu. - * Prompts for a file and restores bookmarks to those in the file. - */ - onRestoreBookmarksFromFile: function PO_onRestoreBookmarksFromFile() { - let dirSvc = Cc["@mozilla.org/file/directory_service;1"]. - getService(Ci.nsIProperties); - let backupsDir = dirSvc.get("Desk", Ci.nsILocalFile); - let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker); - let fpCallback = function fpCallback_done(aResult) { - if (aResult != Ci.nsIFilePicker.returnCancel) { - this.restoreBookmarksFromFile(fp.file); - } - }.bind(this); - - fp.init(window, PlacesUIUtils.getString("bookmarksRestoreTitle"), - Ci.nsIFilePicker.modeOpen); - fp.appendFilter(PlacesUIUtils.getString("bookmarksRestoreFilterName"), - RESTORE_FILEPICKER_FILTER_EXT); - fp.appendFilters(Ci.nsIFilePicker.filterAll); - fp.displayDirectory = backupsDir; - fp.open(fpCallback); - }, - - /** - * Restores bookmarks from a JSON file. - */ - restoreBookmarksFromFile: function PO_restoreBookmarksFromFile(aFile) { - // check file extension - let filePath = aFile.path; - if (!filePath.toLowerCase().endsWith("json") && - !filePath.toLowerCase().endsWith("jsonlz4")) { - this._showErrorAlert(PlacesUIUtils.getString("bookmarksRestoreFormatError")); - return; - } - - // confirm ok to delete existing bookmarks - var prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"]. - getService(Ci.nsIPromptService); - if (!prompts.confirm(null, - PlacesUIUtils.getString("bookmarksRestoreAlertTitle"), - PlacesUIUtils.getString("bookmarksRestoreAlert"))) - return; - - Task.spawn(function() { - try { - yield BookmarkJSONUtils.importFromFile(aFile.path, true); - } catch(ex) { - PlacesOrganizer._showErrorAlert(PlacesUIUtils.getString("bookmarksRestoreParseError")); - } - }); - }, - - _showErrorAlert: function PO__showErrorAlert(aMsg) { - var brandShortName = document.getElementById("brandStrings"). - getString("brandShortName"); - - Cc["@mozilla.org/embedcomp/prompt-service;1"]. - getService(Ci.nsIPromptService). - alert(window, brandShortName, aMsg); - }, - - /** - * Backup bookmarks to desktop, auto-generate a filename with a date. - * The file is a JSON serialization of bookmarks, tags and any annotations - * of those items. - */ - backupBookmarks: function PO_backupBookmarks() { - let dirSvc = Cc["@mozilla.org/file/directory_service;1"]. - getService(Ci.nsIProperties); - let backupsDir = dirSvc.get("Desk", Ci.nsILocalFile); - let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker); - let fpCallback = function fpCallback_done(aResult) { - if (aResult != Ci.nsIFilePicker.returnCancel) { - BookmarkJSONUtils.exportToFile(fp.file.path); - } - }; - - fp.init(window, PlacesUIUtils.getString("bookmarksBackupTitle"), - Ci.nsIFilePicker.modeSave); - fp.appendFilter(PlacesUIUtils.getString("bookmarksRestoreFilterName"), - RESTORE_FILEPICKER_FILTER_EXT); - fp.defaultString = PlacesBackups.getFilenameForDate(); - fp.displayDirectory = backupsDir; - fp.open(fpCallback); - }, - - _paneDisabled: false, - _setDetailsFieldsDisabledState: - function PO__setDetailsFieldsDisabledState(aDisabled) { - if (aDisabled) { - document.getElementById("paneElementsBroadcaster") - .setAttribute("disabled", "true"); - } - else { - document.getElementById("paneElementsBroadcaster") - .removeAttribute("disabled"); - } - }, - - _detectAndSetDetailsPaneMinimalState: - function PO__detectAndSetDetailsPaneMinimalState(aNode) { - /** - * The details of simple folder-items (as opposed to livemarks) or the - * of livemark-children are not likely to fill the infoBox anyway, - * thus we remove the "More/Less" button and show all details. - * - * the wasminimal attribute here is used to persist the "more/less" - * state in a bookmark->folder->bookmark scenario. - */ - var infoBox = document.getElementById("infoBox"); - var infoBoxExpander = document.getElementById("infoBoxExpander"); - var infoBoxExpanderWrapper = document.getElementById("infoBoxExpanderWrapper"); - var additionalInfoBroadcaster = document.getElementById("additionalInfoBroadcaster"); - - if (!aNode) { - infoBoxExpanderWrapper.hidden = true; - return; - } - if (aNode.itemId != -1 && - PlacesUtils.nodeIsFolder(aNode) && !aNode._feedURI) { - if (infoBox.getAttribute("minimal") == "true") - infoBox.setAttribute("wasminimal", "true"); - infoBox.removeAttribute("minimal"); - infoBoxExpanderWrapper.hidden = true; - } - else { - if (infoBox.getAttribute("wasminimal") == "true") - infoBox.setAttribute("minimal", "true"); - infoBox.removeAttribute("wasminimal"); - infoBoxExpanderWrapper.hidden = - this._additionalInfoFields.every(function (id) - document.getElementById(id).collapsed); - } - additionalInfoBroadcaster.hidden = infoBox.getAttribute("minimal") == "true"; - }, - - // NOT YET USED - updateThumbnailProportions: function PO_updateThumbnailProportions() { - var previewBox = document.getElementById("previewBox"); - var canvas = document.getElementById("itemThumbnail"); - var height = previewBox.boxObject.height; - var width = height * (screen.width / screen.height); - canvas.width = width; - canvas.height = height; - }, - - _fillDetailsPane: function PO__fillDetailsPane(aNodeList) { - var infoBox = document.getElementById("infoBox"); - var detailsDeck = document.getElementById("detailsDeck"); - - // Make sure the infoBox UI is visible if we need to use it, we hide it - // below when we don't. - infoBox.hidden = false; - var aSelectedNode = aNodeList.length == 1 ? aNodeList[0] : null; - // If a textbox within a panel is focused, force-blur it so its contents - // are saved - if (gEditItemOverlay.itemId != -1) { - var focusedElement = document.commandDispatcher.focusedElement; - if ((focusedElement instanceof HTMLInputElement || - focusedElement instanceof HTMLTextAreaElement) && - /^editBMPanel.*/.test(focusedElement.parentNode.parentNode.id)) - focusedElement.blur(); - - // don't update the panel if we are already editing this node unless we're - // in multi-edit mode - if (aSelectedNode) { - var concreteId = PlacesUtils.getConcreteItemId(aSelectedNode); - var nodeIsSame = gEditItemOverlay.itemId == aSelectedNode.itemId || - gEditItemOverlay.itemId == concreteId || - (aSelectedNode.itemId == -1 && gEditItemOverlay.uri && - gEditItemOverlay.uri == aSelectedNode.uri); - if (nodeIsSame && detailsDeck.selectedIndex == 1 && - !gEditItemOverlay.multiEdit) - return; - } - } - - // Clean up the panel before initing it again. - gEditItemOverlay.uninitPanel(false); - - if (aSelectedNode && !PlacesUtils.nodeIsSeparator(aSelectedNode)) { - detailsDeck.selectedIndex = 1; - // Using the concrete itemId is arguably wrong. The bookmarks API - // does allow setting properties for folder shortcuts as well, but since - // the UI does not distinct between the couple, we better just show - // the concrete item properties for shortcuts to root nodes. - var concreteId = PlacesUtils.getConcreteItemId(aSelectedNode); - var isRootItem = concreteId != -1 && PlacesUtils.isRootItem(concreteId); - var readOnly = isRootItem || - aSelectedNode.parent.itemId == PlacesUIUtils.leftPaneFolderId; - var useConcreteId = isRootItem || - PlacesUtils.nodeIsTagQuery(aSelectedNode); - var itemId = -1; - if (concreteId != -1 && useConcreteId) - itemId = concreteId; - else if (aSelectedNode.itemId != -1) - itemId = aSelectedNode.itemId; - else - itemId = PlacesUtils._uri(aSelectedNode.uri); - - gEditItemOverlay.initPanel(itemId, { hiddenRows: ["folderPicker"] - , forceReadOnly: readOnly - , titleOverride: aSelectedNode.title - }); - - // Dynamically generated queries, like history date containers, have - // itemId !=0 and do not exist in history. For them the panel is - // read-only, but empty, since it can't get a valid title for the object. - // In such a case we force the title using the selectedNode one, for UI - // polishness. - if (aSelectedNode.itemId == -1 && - (PlacesUtils.nodeIsDay(aSelectedNode) || - PlacesUtils.nodeIsHost(aSelectedNode))) - gEditItemOverlay._element("namePicker").value = aSelectedNode.title; - - this._detectAndSetDetailsPaneMinimalState(aSelectedNode); - } - else if (!aSelectedNode && aNodeList[0]) { - var itemIds = []; - for (var i = 0; i < aNodeList.length; i++) { - if (!PlacesUtils.nodeIsBookmark(aNodeList[i]) && - !PlacesUtils.nodeIsURI(aNodeList[i])) { - detailsDeck.selectedIndex = 0; - var selectItemDesc = document.getElementById("selectItemDescription"); - var itemsCountLabel = document.getElementById("itemsCountText"); - selectItemDesc.hidden = false; - itemsCountLabel.value = - PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel", - aNodeList.length, [aNodeList.length]); - infoBox.hidden = true; - return; - } - itemIds[i] = aNodeList[i].itemId != -1 ? aNodeList[i].itemId : - PlacesUtils._uri(aNodeList[i].uri); - } - detailsDeck.selectedIndex = 1; - gEditItemOverlay.initPanel(itemIds, - { hiddenRows: ["folderPicker", - "loadInSidebar", - "location", - "keyword", - "description", - "name"]}); - this._detectAndSetDetailsPaneMinimalState(aSelectedNode); - } - else { - detailsDeck.selectedIndex = 0; - infoBox.hidden = true; - let selectItemDesc = document.getElementById("selectItemDescription"); - let itemsCountLabel = document.getElementById("itemsCountText"); - let itemsCount = 0; - if (ContentArea.currentView.result) { - let rootNode = ContentArea.currentView.result.root; - if (rootNode.containerOpen) - itemsCount = rootNode.childCount; - } - if (itemsCount == 0) { - selectItemDesc.hidden = true; - itemsCountLabel.value = PlacesUIUtils.getString("detailsPane.noItems"); - } - else { - selectItemDesc.hidden = false; - itemsCountLabel.value = - PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel", - itemsCount, [itemsCount]); - } - } - }, - - // NOT YET USED - _updateThumbnail: function PO__updateThumbnail() { - var bo = document.getElementById("previewBox").boxObject; - var width = bo.width; - var height = bo.height; - - var canvas = document.getElementById("itemThumbnail"); - var ctx = canvas.getContext('2d'); - var notAvailableText = canvas.getAttribute("notavailabletext"); - ctx.save(); - ctx.fillStyle = "-moz-Dialog"; - ctx.fillRect(0, 0, width, height); - ctx.translate(width/2, height/2); - - ctx.fillStyle = "GrayText"; - ctx.mozTextStyle = "12pt sans serif"; - var len = ctx.mozMeasureText(notAvailableText); - ctx.translate(-len/2,0); - ctx.mozDrawText(notAvailableText); - ctx.restore(); - }, - - toggleAdditionalInfoFields: function PO_toggleAdditionalInfoFields() { - var infoBox = document.getElementById("infoBox"); - var infoBoxExpander = document.getElementById("infoBoxExpander"); - var infoBoxExpanderLabel = document.getElementById("infoBoxExpanderLabel"); - var additionalInfoBroadcaster = document.getElementById("additionalInfoBroadcaster"); - - if (infoBox.getAttribute("minimal") == "true") { - infoBox.removeAttribute("minimal"); - infoBoxExpanderLabel.value = infoBoxExpanderLabel.getAttribute("lesslabel"); - infoBoxExpanderLabel.accessKey = infoBoxExpanderLabel.getAttribute("lessaccesskey"); - infoBoxExpander.className = "expander-up"; - additionalInfoBroadcaster.removeAttribute("hidden"); - } - else { - infoBox.setAttribute("minimal", "true"); - infoBoxExpanderLabel.value = infoBoxExpanderLabel.getAttribute("morelabel"); - infoBoxExpanderLabel.accessKey = infoBoxExpanderLabel.getAttribute("moreaccesskey"); - infoBoxExpander.className = "expander-down"; - additionalInfoBroadcaster.setAttribute("hidden", "true"); - } - }, - - /** - * Save the current search (or advanced query) to the bookmarks root. - */ - saveSearch: function PO_saveSearch() { - // Get the place: uri for the query. - // If the advanced query builder is showing, use that. - var options = this.getCurrentOptions(); - var queries = this.getCurrentQueries(); - - var placeSpec = PlacesUtils.history.queriesToQueryString(queries, - queries.length, - options); - var placeURI = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService). - newURI(placeSpec, null, null); - - // Prompt the user for a name for the query. - // XXX - using prompt service for now; will need to make - // a real dialog and localize when we're sure this is the UI we want. - var title = PlacesUIUtils.getString("saveSearch.title"); - var inputLabel = PlacesUIUtils.getString("saveSearch.inputLabel"); - var defaultText = PlacesUIUtils.getString("saveSearch.inputDefaultText"); - - var prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"]. - getService(Ci.nsIPromptService); - var check = {value: false}; - var input = {value: defaultText}; - var save = prompts.prompt(null, title, inputLabel, input, null, check); - - // Don't add the query if the user cancels or clears the seach name. - if (!save || input.value == "") - return; - - // Add the place: uri as a bookmark under the bookmarks root. - var txn = new PlacesCreateBookmarkTransaction(placeURI, - PlacesUtils.bookmarksMenuFolderId, - PlacesUtils.bookmarks.DEFAULT_INDEX, - input.value); - PlacesUtils.transactionManager.doTransaction(txn); - - // select and load the new query - this._places.selectPlaceURI(placeSpec); - } -}; - -/** - * A set of utilities relating to search within Bookmarks and History. - */ -var PlacesSearchBox = { - - /** - * The Search text field - */ - get searchFilter() { - return document.getElementById("searchFilter"); - }, - - /** - * Folders to include when searching. - */ - _folders: [], - get folders() { - if (this._folders.length == 0) { - this._folders.push(PlacesUtils.bookmarksMenuFolderId, - PlacesUtils.unfiledBookmarksFolderId, - PlacesUtils.toolbarFolderId); - } - return this._folders; - }, - set folders(aFolders) { - this._folders = aFolders; - return aFolders; - }, - - /** - * Run a search for the specified text, over the collection specified by - * the dropdown arrow. The default is all bookmarks, but can be - * localized to the active collection. - * @param filterString - * The text to search for. - */ - search: function PSB_search(filterString) { - var PO = PlacesOrganizer; - // If the user empties the search box manually, reset it and load all - // contents of the current scope. - // XXX this might be to jumpy, maybe should search for "", so results - // are ungrouped, and search box not reset - if (filterString == "") { - PO.onPlaceSelected(false); - return; - } - - let currentView = ContentArea.currentView; - let currentOptions = PO.getCurrentOptions(); - - // Search according to the current scope and folders, which were set by - // PQB_setScope() - switch (PlacesSearchBox.filterCollection) { - case "collection": - currentView.applyFilter(filterString, this.folders); - break; - case "bookmarks": - currentView.applyFilter(filterString, this.folders); - break; - case "history": - if (currentOptions.queryType != Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) { - var query = PlacesUtils.history.getNewQuery(); - query.searchTerms = filterString; - var options = currentOptions.clone(); - // Make sure we're getting uri results. - options.resultType = currentOptions.RESULTS_AS_URI; - options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY; - options.includeHidden = true; - currentView.load([query], options); - } - else { - currentView.applyFilter(filterString, null, true); - } - break; - case "downloads": - if (currentView == ContentTree.view) { - let query = PlacesUtils.history.getNewQuery(); - query.searchTerms = filterString; - query.setTransitions([Ci.nsINavHistoryService.TRANSITION_DOWNLOAD], 1); - let options = currentOptions.clone(); - // Make sure we're getting uri results. - options.resultType = currentOptions.RESULTS_AS_URI; - options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY; - options.includeHidden = true; - currentView.load([query], options); - } - else { - // The new downloads view doesn't use places for searching downloads. - currentView.searchTerm = filterString; - } - break; - default: - throw new Components.Exception("Invalid filterCollection on search", - Components.results.NS_ERROR_INVALID_ARG); - } - - PlacesSearchBox.showSearchUI(); - - // Update the details panel - PlacesOrganizer.updateDetailsPane(); - }, - - /** - * Finds across all history, downloads or all bookmarks. - */ - findAll: function PSB_findAll() { - switch (this.filterCollection) { - case "history": - PlacesQueryBuilder.setScope("history"); - break; - case "downloads": - PlacesQueryBuilder.setScope("downloads"); - break; - default: - PlacesQueryBuilder.setScope("bookmarks"); - break; - } - this.focus(); - }, - - /** - * Updates the display with the title of the current collection. - * @param aTitle - * The title of the current collection. - */ - updateCollectionTitle: function PSB_updateCollectionTitle(aTitle) { - let title = ""; - // This is needed when a user performs a folder-specific search - // using the scope bar, removes the search-string, and unfocuses - // the search box, at least until the removal of the scope bar. - if (aTitle) { - title = PlacesUIUtils.getFormattedString("searchCurrentDefault", - [aTitle]); - } - else { - switch (this.filterCollection) { - case "history": - title = PlacesUIUtils.getString("searchHistory"); - break; - case "downloads": - title = PlacesUIUtils.getString("searchDownloads"); - break; - default: - title = PlacesUIUtils.getString("searchBookmarks"); - } - } - this.searchFilter.placeholder = title; - }, - - /** - * Gets/sets the active collection from the dropdown menu. - */ - get filterCollection() { - return this.searchFilter.getAttribute("collection"); - }, - set filterCollection(collectionName) { - if (collectionName == this.filterCollection) - return collectionName; - - this.searchFilter.setAttribute("collection", collectionName); - - var newGrayText = null; - if (collectionName == "collection") { - newGrayText = PlacesOrganizer._places.selectedNode.title || - document.getElementById("scopeBarFolder"). - getAttribute("emptytitle"); - } - this.updateCollectionTitle(newGrayText); - return collectionName; - }, - - /** - * Focus the search box - */ - focus: function PSB_focus() { - this.searchFilter.focus(); - }, - - /** - * Set up the gray text in the search bar as the Places View loads. - */ - init: function PSB_init() { - this.updateCollectionTitle(); - }, - - /** - * Gets or sets the text shown in the Places Search Box - */ - get value() { - return this.searchFilter.value; - }, - set value(value) { - return this.searchFilter.value = value; - }, - - showSearchUI: function PSB_showSearchUI() { - // Hide the advanced search controls when the user hasn't searched - var searchModifiers = document.getElementById("searchModifiers"); - searchModifiers.hidden = false; - }, - - hideSearchUI: function PSB_hideSearchUI() { - var searchModifiers = document.getElementById("searchModifiers"); - searchModifiers.hidden = true; - } -}; - -/** - * Functions and data for advanced query builder - */ -var PlacesQueryBuilder = { - - queries: [], - queryOptions: null, - - /** - * Called when a scope button in the scope bar is clicked. - * @param aButton - * the scope button that was selected - */ - onScopeSelected: function PQB_onScopeSelected(aButton) { - switch (aButton.id) { - case "scopeBarHistory": - this.setScope("history"); - break; - case "scopeBarFolder": - this.setScope("collection"); - break; - case "scopeBarDownloads": - this.setScope("downloads"); - break; - case "scopeBarAll": - this.setScope("bookmarks"); - break; - default: - throw new Components.Exception("Invalid search scope button ID", - Components.results.NS_ERROR_INVALID_ARG); - break; - } - }, - - /** - * Sets the search scope. This can be called when no search is active, and - * in that case, when the user does begin a search aScope will be used (see - * PSB_search()). If there is an active search, it's performed again to - * update the content tree. - * @param aScope - * The search scope: "bookmarks", "collection", "downloads" or - * "history". - */ - setScope: function PQB_setScope(aScope) { - // Determine filterCollection, folders, and scopeButtonId based on aScope. - var filterCollection; - var folders = []; - var scopeButtonId; - switch (aScope) { - case "history": - filterCollection = "history"; - scopeButtonId = "scopeBarHistory"; - break; - case "collection": - // The folder scope button can only become hidden upon selecting a new - // folder in the left pane, and the disabled state will remain unchanged - // until a new folder is selected. See PO__setScopeForNode(). - if (!document.getElementById("scopeBarFolder").hidden) { - filterCollection = "collection"; - scopeButtonId = "scopeBarFolder"; - folders.push(PlacesUtils.getConcreteItemId( - PlacesOrganizer._places.selectedNode)); - break; - } - // Fall through. If collection scope doesn't make sense for the - // selected node, choose bookmarks scope. - case "bookmarks": - filterCollection = "bookmarks"; - scopeButtonId = "scopeBarAll"; - folders.push(PlacesUtils.bookmarksMenuFolderId, - PlacesUtils.toolbarFolderId, - PlacesUtils.unfiledBookmarksFolderId); - break; - case "downloads": - filterCollection = "downloads"; - scopeButtonId = "scopeBarDownloads"; - break; - default: - throw new Components.Exception("Invalid search scope", - Components.results.NS_ERROR_INVALID_ARG); - break; - } - - // Check the appropriate scope button in the scope bar. - document.getElementById(scopeButtonId).checked = true; - - // Update the search box. Re-search if there's an active search. - PlacesSearchBox.filterCollection = filterCollection; - PlacesSearchBox.folders = folders; - var searchStr = PlacesSearchBox.searchFilter.value; - if (searchStr) - PlacesSearchBox.search(searchStr); - } -}; - -/** - * Population and commands for the View Menu. - */ -var ViewMenu = { - /** - * Removes content generated previously from a menupopup. - * @param popup - * The popup that contains the previously generated content. - * @param startID - * The id attribute of an element that is the start of the - * dynamically generated region - remove elements after this - * item only. - * Must be contained by popup. Can be null (in which case the - * contents of popup are removed). - * @param endID - * The id attribute of an element that is the end of the - * dynamically generated region - remove elements up to this - * item only. - * Must be contained by popup. Can be null (in which case all - * items until the end of the popup will be removed). Ignored - * if startID is null. - * @returns The element for the caller to insert new items before, - * null if the caller should just append to the popup. - */ - _clean: function VM__clean(popup, startID, endID) { - if (endID) - NS_ASSERT(startID, "meaningless to have valid endID and null startID"); - if (startID) { - var startElement = document.getElementById(startID); - NS_ASSERT(startElement.parentNode == - popup, "startElement is not in popup"); - NS_ASSERT(startElement, - "startID does not correspond to an existing element"); - var endElement = null; - if (endID) { - endElement = document.getElementById(endID); - NS_ASSERT(endElement.parentNode == popup, - "endElement is not in popup"); - NS_ASSERT(endElement, - "endID does not correspond to an existing element"); - } - while (startElement.nextSibling != endElement) - popup.removeChild(startElement.nextSibling); - return endElement; - } - else { - while(popup.hasChildNodes()) - popup.removeChild(popup.firstChild); - } - return null; - }, - - /** - * Fills a menupopup with a list of columns - * @param event - * The popupshowing event that invoked this function. - * @param startID - * see _clean - * @param endID - * see _clean - * @param type - * the type of the menuitem, e.g. "radio" or "checkbox". - * Can be null (no-type). - * Checkboxes are checked if the column is visible. - * @param propertyPrefix - * If propertyPrefix is non-null: - * propertyPrefix + column ID + ".label" will be used to get the - * localized label string. - * propertyPrefix + column ID + ".accesskey" will be used to get the - * localized accesskey. - * If propertyPrefix is null, the column label is used as label and - * no accesskey is assigned. - */ - fillWithColumns: function VM_fillWithColumns(event, startID, endID, type, propertyPrefix) { - var popup = event.target; - var pivot = this._clean(popup, startID, endID); - - // If no column is "sort-active", the "Unsorted" item needs to be checked, - // so track whether or not we find a column that is sort-active. - var isSorted = false; - var content = document.getElementById("placeContent"); - var columns = content.columns; - for (var i = 0; i < columns.count; ++i) { - var column = columns.getColumnAt(i).element; - if (popup.parentNode && (popup.parentNode.id == "viewSort")) { - switch (column.id) { - case "placesContentParentFolder": - continue; - case "placesContentParentFolderPath": - continue; - } - } - var menuitem = document.createElement("menuitem"); - menuitem.id = "menucol_" + column.id; - menuitem.column = column; - var label = column.getAttribute("label"); - if (propertyPrefix) { - var menuitemPrefix = propertyPrefix; - // for string properties, use "name" as the id, instead of "title" - // see bug #386287 for details - var columnId = column.getAttribute("anonid"); - menuitemPrefix += columnId == "title" ? "name" : columnId; - label = PlacesUIUtils.getString(menuitemPrefix + ".label"); - var accesskey = PlacesUIUtils.getString(menuitemPrefix + ".accesskey"); - menuitem.setAttribute("accesskey", accesskey); - } - menuitem.setAttribute("label", label); - if (type == "radio") { - menuitem.setAttribute("type", "radio"); - menuitem.setAttribute("name", "columns"); - // This column is the sort key. Its item is checked. - if (column.getAttribute("sortDirection") != "") { - menuitem.setAttribute("checked", "true"); - isSorted = true; - } - } - else if (type == "checkbox") { - menuitem.setAttribute("type", "checkbox"); - // Cannot uncheck the primary column. - if (column.getAttribute("primary") == "true") - menuitem.setAttribute("disabled", "true"); - // Items for visible columns are checked. - if (!column.hidden) - menuitem.setAttribute("checked", "true"); - } - if (pivot) - popup.insertBefore(menuitem, pivot); - else - popup.appendChild(menuitem); - } - event.stopPropagation(); - }, - - /** - * Set up the content of the view menu. - */ - populateSortMenu: function VM_populateSortMenu(event) { - this.fillWithColumns(event, "viewUnsorted", "directionSeparator", "radio", "view.sortBy."); - - var sortColumn = this._getSortColumn(); - var viewSortAscending = document.getElementById("viewSortAscending"); - var viewSortDescending = document.getElementById("viewSortDescending"); - // We need to remove an existing checked attribute because the unsorted - // menu item is not rebuilt every time we open the menu like the others. - var viewUnsorted = document.getElementById("viewUnsorted"); - if (!sortColumn) { - viewSortAscending.removeAttribute("checked"); - viewSortDescending.removeAttribute("checked"); - viewUnsorted.setAttribute("checked", "true"); - } - else if (sortColumn.getAttribute("sortDirection") == "ascending") { - viewSortAscending.setAttribute("checked", "true"); - viewSortDescending.removeAttribute("checked"); - viewUnsorted.removeAttribute("checked"); - } - else if (sortColumn.getAttribute("sortDirection") == "descending") { - viewSortDescending.setAttribute("checked", "true"); - viewSortAscending.removeAttribute("checked"); - viewUnsorted.removeAttribute("checked"); - } - }, - - /** - * Shows/Hides a tree column. - * @param element - * The menuitem element for the column - */ - showHideColumn: function VM_showHideColumn(element) { - var column = element.column; - - var splitter = column.nextSibling; - if (splitter && splitter.localName != "splitter") - splitter = null; - - if (element.getAttribute("checked") == "true") { - column.setAttribute("hidden", "false"); - if (splitter) - splitter.removeAttribute("hidden"); - } - else { - column.setAttribute("hidden", "true"); - if (splitter) - splitter.setAttribute("hidden", "true"); - } - }, - - /** - * Gets the last column that was sorted. - * @returns the currently sorted column, null if there is no sorted column. - */ - _getSortColumn: function VM__getSortColumn() { - var content = document.getElementById("placeContent"); - var cols = content.columns; - for (var i = 0; i < cols.count; ++i) { - var column = cols.getColumnAt(i).element; - var sortDirection = column.getAttribute("sortDirection"); - if (sortDirection == "ascending" || sortDirection == "descending") - return column; - } - return null; - }, - - /** - * Sorts the view by the specified column. - * @param aColumn - * The colum that is the sort key. Can be null - the - * current sort column or the title column will be used. - * @param aDirection - * The direction to sort - "ascending" or "descending". - * Can be null - the last direction or descending will be used. - * - * If both aColumnID and aDirection are null, the view will be unsorted. - */ - setSortColumn: function VM_setSortColumn(aColumn, aDirection) { - var result = document.getElementById("placeContent").result; - if (!aColumn && !aDirection) { - result.sortingMode = Ci.nsINavHistoryQueryOptions.SORT_BY_NONE; - return; - } - - var columnId; - if (aColumn) { - columnId = aColumn.getAttribute("anonid"); - if (!aDirection) { - var sortColumn = this._getSortColumn(); - if (sortColumn) - aDirection = sortColumn.getAttribute("sortDirection"); - } - } - else { - var sortColumn = this._getSortColumn(); - columnId = sortColumn ? sortColumn.getAttribute("anonid") : "title"; - } - - // This maps the possible values of columnId (i.e., anonid's of treecols in - // placeContent) to the default sortingMode and sortingAnnotation values for - // each column. - // key: Sort key in the name of one of the - // nsINavHistoryQueryOptions.SORT_BY_* constants - // dir: Default sort direction to use if none has been specified - // anno: The annotation to sort by, if key is "ANNOTATION" - var colLookupTable = { - title: { key: "TITLE", dir: "ascending" }, - tags: { key: "TAGS", dir: "ascending" }, - url: { key: "URI", dir: "ascending" }, - date: { key: "DATE", dir: "descending" }, - visitCount: { key: "VISITCOUNT", dir: "descending" }, - keyword: { key: "KEYWORD", dir: "ascending" }, - dateAdded: { key: "DATEADDED", dir: "descending" }, - lastModified: { key: "LASTMODIFIED", dir: "descending" }, - description: { key: "ANNOTATION", - dir: "ascending", - anno: PlacesUIUtils.DESCRIPTION_ANNO } - }; - - // Make sure we have a valid column. - if (!colLookupTable.hasOwnProperty(columnId)) - throw new Components.Exception("Invalid column", - Components.results.NS_ERROR_INVALID_ARG); - - // Use a default sort direction if none has been specified. If aDirection - // is invalid, result.sortingMode will be undefined, which has the effect - // of unsorting the tree. - aDirection = (aDirection || colLookupTable[columnId].dir).toUpperCase(); - - var sortConst = "SORT_BY_" + colLookupTable[columnId].key + "_" + aDirection; - result.sortingAnnotation = colLookupTable[columnId].anno || ""; - result.sortingMode = Ci.nsINavHistoryQueryOptions[sortConst]; - } -} - -var ContentArea = { - _specialViews: new Map(), - - init: function CA_init() { - this._deck = document.getElementById("placesViewsDeck"); - this._toolbar = document.getElementById("placesToolbar"); - ContentTree.init(); - this._setupView(); - }, - - /** - * Gets the content view to be used for loading the given query. - * If a custom view was set by setContentViewForQueryString, that - * view would be returned, else the default tree view is returned - * - * @param aQueryString - * a query string - * @return the view to be used for loading aQueryString. - */ - getContentViewForQueryString: - function CA_getContentViewForQueryString(aQueryString) { - try { - if (this._specialViews.has(aQueryString)) { - let { view, options } = this._specialViews.get(aQueryString); - if (typeof view == "function") { - view = view(); - this._specialViews.set(aQueryString, { view: view, options: options }); - } - return view; - } - } - catch(ex) { - Components.utils.reportError(ex); - } - return ContentTree.view; - }, - - /** - * Sets a custom view to be used rather than the default places tree - * whenever the given query is selected in the left pane. - * @param aQueryString - * a query string - * @param aView - * Either the custom view or a function that will return the view - * the first (and only) time it's called. - * @param [optional] aOptions - * Object defining special options for the view. - * @see ContentTree.viewOptions for supported options and default values. - */ - setContentViewForQueryString: - function CA_setContentViewForQueryString(aQueryString, aView, aOptions) { - if (!aQueryString || - typeof aView != "object" && typeof aView != "function") - throw new Components.Exception("Invalid arguments", - Components.results.NS_ERROR_INVALID_ARG); - - this._specialViews.set(aQueryString, { view: aView, - options: aOptions || new Object() }); - }, - - get currentView() PlacesUIUtils.getViewForNode(this._deck.selectedPanel), - set currentView(aNewView) { - let oldView = this.currentView; - if (oldView != aNewView) { - this._deck.selectedPanel = aNewView.associatedElement; - - // If the content area inactivated view was focused, move focus - // to the new view. - if (document.activeElement == oldView.associatedElement) - aNewView.associatedElement.focus(); - } - return aNewView; - }, - - get currentPlace() this.currentView.place, - set currentPlace(aQueryString) { - let oldView = this.currentView; - let newView = this.getContentViewForQueryString(aQueryString); - newView.place = aQueryString; - if (oldView != newView) { - oldView.active = false; - this.currentView = newView; - this._setupView(); - newView.active = true; - } - return aQueryString; - }, - - /** - * Applies view options. - */ - _setupView: function CA__setupView() { - let options = this.currentViewOptions; - - // showDetailsPane. - let detailsDeck = document.getElementById("detailsDeck"); - detailsDeck.hidden = !options.showDetailsPane; - - // toolbarSet. - for (let elt of this._toolbar.childNodes) { - // On Windows and Linux the menu buttons are menus wrapped in a menubar. - if (elt.id == "placesMenu") { - for (let menuElt of elt.childNodes) { - menuElt.hidden = options.toolbarSet.indexOf(menuElt.id) == -1; - } - } - else { - elt.hidden = options.toolbarSet.indexOf(elt.id) == -1; - } - } - }, - - /** - * Options for the current view. - * - * @see ContentTree.viewOptions for supported options and default values. - */ - get currentViewOptions() { - // Use ContentTree options as default. - let viewOptions = ContentTree.viewOptions; - if (this._specialViews.has(this.currentPlace)) { - let { view, options } = this._specialViews.get(this.currentPlace); - for (let option in options) { - viewOptions[option] = options[option]; - } - } - return viewOptions; - }, - - focus: function() { - this._deck.selectedPanel.focus(); - } -}; - -var ContentTree = { - init: function CT_init() { - this._view = document.getElementById("placeContent"); - }, - - get view() this._view, - - get viewOptions() Object.seal({ - showDetailsPane: true, - toolbarSet: "back-button, forward-button, organizeButton, viewMenu, maintenanceButton, libraryToolbarSpacer, searchFilter" - }), - - openSelectedNode: function CT_openSelectedNode(aEvent) { - let view = this.view; - PlacesUIUtils.openNodeWithEvent(view.selectedNode, aEvent, view); - }, - - onClick: function CT_onClick(aEvent) { - let node = this.view.selectedNode; - if (node) { - let doubleClick = aEvent.button == 0 && aEvent.detail == 2; - let middleClick = aEvent.button == 1 && aEvent.detail == 1; - if (PlacesUtils.nodeIsURI(node) && (doubleClick || middleClick)) { - // Open associated uri in the browser. - this.openSelectedNode(aEvent); - } - else if (middleClick && PlacesUtils.nodeIsContainer(node)) { - // The command execution function will take care of seeing if the - // selection is a folder or a different container type, and will - // load its contents in tabs. - PlacesUIUtils.openContainerNodeInTabs(node, aEvent, this.view); - } - } - }, - - onKeyPress: function CT_onKeyPress(aEvent) { - if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN) - this.openSelectedNode(aEvent); - } -}; diff --git a/components/places/content/places.xul b/components/places/content/places.xul deleted file mode 100644 index 92e8a70..0000000 --- a/components/places/content/places.xul +++ /dev/null @@ -1,471 +0,0 @@ -<?xml version="1.0"?> - -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -<?xml-stylesheet href="chrome://browser/content/places/places.css"?> -<?xml-stylesheet href="chrome://browser/content/places/organizer.css"?> - -<?xml-stylesheet href="chrome://global/skin/"?> -<?xml-stylesheet href="chrome://browser/skin/places/places.css"?> -<?xml-stylesheet href="chrome://browser/skin/places/organizer.css"?> - -<?xul-overlay href="chrome://browser/content/places/editBookmarkOverlay.xul"?> - -#ifdef XP_MACOSX -<?xul-overlay href="chrome://browser/content/macBrowserOverlay.xul"?> -#else -<?xul-overlay href="chrome://browser/content/baseMenuOverlay.xul"?> -<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?> -<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?> -#endif - -<!DOCTYPE window [ -<!ENTITY % placesDTD SYSTEM "chrome://browser/locale/places/places.dtd"> -%placesDTD; -<!ENTITY % editMenuOverlayDTD SYSTEM "chrome://global/locale/editMenuOverlay.dtd"> -%editMenuOverlayDTD; -<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd"> -%browserDTD; -]> - -<window id="places" - title="&places.library.title;" - windowtype="Places:Organizer" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:html="http://www.w3.org/1999/xhtml" - onload="PlacesOrganizer.init();" - onunload="PlacesOrganizer.destroy();" - width="&places.library.width;" height="&places.library.height;" - screenX="10" screenY="10" - toggletoolbar="true" - persist="width height screenX screenY sizemode"> - - <script type="application/javascript" - src="chrome://browser/content/places/places.js"/> - <script type="application/javascript" - src="chrome://browser/content/utilityOverlay.js"/> - <script type="application/javascript" - src="chrome://browser/content/places/editBookmarkOverlay.js"/> - - <stringbundleset id="placesStringSet"> - <stringbundle id="brandStrings" src="chrome://branding/locale/brand.properties"/> - </stringbundleset> - -#ifdef XP_MACOSX -#include ../../../base/content/browserMountPoints.inc -#else - <commandset id="editMenuCommands"/> - <commandset id="placesCommands"/> -#endif - <keyset id="placesCommandKeys"/> - - <commandset id="organizerCommandSet"> - <command id="OrganizerCommand_find:all" - oncommand="PlacesSearchBox.findAll();"/> - <command id="OrganizerCommand_export" - oncommand="PlacesOrganizer.exportBookmarks();"/> - <command id="OrganizerCommand_import" - oncommand="PlacesOrganizer.importFromFile();"/> - <command id="OrganizerCommand_backup" - oncommand="PlacesOrganizer.backupBookmarks();"/> - <command id="OrganizerCommand_restoreFromFile" - oncommand="PlacesOrganizer.onRestoreBookmarksFromFile();"/> - <command id="OrganizerCommand_search:save" - oncommand="PlacesOrganizer.saveSearch();"/> - <command id="OrganizerCommand_search:moreCriteria" - oncommand="PlacesQueryBuilder.addRow();"/> - <command id="OrganizerCommand:Back" - oncommand="PlacesOrganizer.back();"/> - <command id="OrganizerCommand:Forward" - oncommand="PlacesOrganizer.forward();"/> - </commandset> - - <keyset id="placesOrganizerKeyset"> - <!-- Instantiation Keys --> - <key id="placesKey_close" key="&cmd.close.key;" modifiers="accel" - oncommand="close();"/> - - <!-- Command Keys --> - <key id="placesKey_find:all" - command="OrganizerCommand_find:all" - key="&cmd.find.key;" - modifiers="accel"/> - - <!-- Back/Forward Keys Support --> -#ifndef XP_MACOSX - <key id="placesKey_goBackKb" - keycode="VK_LEFT" - command="OrganizerCommand:Back" - modifiers="alt"/> - <key id="placesKey_goForwardKb" - keycode="VK_RIGHT" - command="OrganizerCommand:Forward" - modifiers="alt"/> -#else - <key id="placesKey_goBackKb" - keycode="VK_LEFT" - command="OrganizerCommand:Back" - modifiers="accel"/> - <key id="placesKey_goForwardKb" - keycode="VK_RIGHT" - command="OrganizerCommand:Forward" - modifiers="accel"/> -#endif -#ifdef XP_UNIX - <key id="placesKey_goBackKb2" - key="&goBackCmd.commandKey;" - command="OrganizerCommand:Back" - modifiers="accel"/> - <key id="placesKey_goForwardKb2" - key="&goForwardCmd.commandKey;" - command="OrganizerCommand:Forward" - modifiers="accel"/> -#endif - </keyset> - - <keyset id="editMenuKeys"> -#ifdef XP_MACOSX - <key id="key_delete2" keycode="VK_BACK" command="cmd_delete"/> -#endif - </keyset> - - <popupset id="placesPopupset"> - <menupopup id="placesContext"/> - <menupopup id="placesColumnsContext" - onpopupshowing="ViewMenu.fillWithColumns(event, null, null, 'checkbox', null);" - oncommand="ViewMenu.showHideColumn(event.target); event.stopPropagation();"/> - </popupset> - - <toolbox id="placesToolbox"> - <toolbar class="chromeclass-toolbar" id="placesToolbar" align="center"> - <toolbarbutton id="back-button" - command="OrganizerCommand:Back" - tooltiptext="&backButton.tooltip;" - disabled="true"/> - - <toolbarbutton id="forward-button" - command="OrganizerCommand:Forward" - tooltiptext="&forwardButton.tooltip;" - disabled="true"/> - -#ifdef XP_MACOSX - <toolbarbutton type="menu" class="tabbable" - onpopupshowing="document.getElementById('placeContent').focus()" -#else - <menubar id="placesMenu"> - <menu accesskey="&organize.accesskey;" class="menu-iconic" -#endif - id="organizeButton" label="&organize.label;" - tooltiptext="&organize.tooltip;"> - <menupopup id="organizeButtonPopup"> - <menuitem id="newbookmark" - command="placesCmd_new:bookmark" - label="&cmd.new_bookmark.label;" - accesskey="&cmd.new_bookmark.accesskey;"/> - <menuitem id="newfolder" - command="placesCmd_new:folder" - label="&cmd.new_folder.label;" - accesskey="&cmd.new_folder.accesskey;"/> - <menuitem id="newseparator" - command="placesCmd_new:separator" - label="&cmd.new_separator.label;" - accesskey="&cmd.new_separator.accesskey;"/> - -#ifndef XP_MACOSX - <menuseparator id="orgUndoSeparator"/> - - <menuitem id="orgUndo" - command="cmd_undo" - label="&undoCmd.label;" - key="key_undo" - accesskey="&undoCmd.accesskey;"/> - <menuitem id="orgRedo" - command="cmd_redo" - label="&redoCmd.label;" - key="key_redo" - accesskey="&redoCmd.accesskey;"/> - - <menuseparator id="orgCutSeparator"/> - - <menuitem id="orgCut" - command="cmd_cut" - label="&cutCmd.label;" - key="key_cut" - accesskey="&cutCmd.accesskey;" - selection="separator|link|folder|mixed"/> - <menuitem id="orgCopy" - command="cmd_copy" - label="©Cmd.label;" - key="key_copy" - accesskey="©Cmd.accesskey;" - selection="separator|link|folder|mixed"/> - <menuitem id="orgPaste" - command="cmd_paste" - label="&pasteCmd.label;" - key="key_paste" - accesskey="&pasteCmd.accesskey;" - selection="mutable"/> - <menuitem id="orgDelete" - command="cmd_delete" - label="&deleteCmd.label;" - key="key_delete" - accesskey="&deleteCmd.accesskey;"/> - - <menuseparator id="selectAllSeparator"/> - - <menuitem id="orgSelectAll" - command="cmd_selectAll" - label="&selectAllCmd.label;" - key="key_selectAll" - accesskey="&selectAllCmd.accesskey;"/> - -#endif - <menuseparator id="orgMoveSeparator"/> - - <menuitem id="orgMoveBookmarks" - command="placesCmd_moveBookmarks" - label="&cmd.moveBookmarks.label;" - accesskey="&cmd.moveBookmarks.accesskey;"/> -#ifdef XP_MACOSX - <menuitem id="orgDelete" - command="cmd_delete" - label="&deleteCmd.label;" - key="key_delete" - accesskey="&deleteCmd.accesskey;"/> -#else - <menuseparator id="orgCloseSeparator"/> - - <menuitem id="orgClose" - key="placesKey_close" - label="&file.close.label;" - accesskey="&file.close.accesskey;" - oncommand="close();"/> -#endif - </menupopup> -#ifdef XP_MACOSX - </toolbarbutton> - <toolbarbutton type="menu" class="tabbable" -#else - </menu> - <menu accesskey="&views.accesskey;" class="menu-iconic" -#endif - id="viewMenu" label="&views.label;" - tooltiptext="&views.tooltip;"> - <menupopup id="viewMenuPopup"> - - <menu id="viewColumns" - label="&view.columns.label;" accesskey="&view.columns.accesskey;"> - <menupopup onpopupshowing="ViewMenu.fillWithColumns(event, null, null, 'checkbox', null);" - oncommand="ViewMenu.showHideColumn(event.target); event.stopPropagation();"/> - </menu> - - <menu id="viewSort" label="&view.sort.label;" - accesskey="&view.sort.accesskey;"> - <menupopup onpopupshowing="ViewMenu.populateSortMenu(event);" - oncommand="ViewMenu.setSortColumn(event.target.column, null);"> - <menuitem id="viewUnsorted" type="radio" name="columns" - label="&view.unsorted.label;" accesskey="&view.unsorted.accesskey;" - oncommand="ViewMenu.setSortColumn(null, null);"/> - <menuseparator id="directionSeparator"/> - <menuitem id="viewSortAscending" type="radio" name="direction" - label="&view.sortAscending.label;" accesskey="&view.sortAscending.accesskey;" - oncommand="ViewMenu.setSortColumn(null, 'ascending'); event.stopPropagation();"/> - <menuitem id="viewSortDescending" type="radio" name="direction" - label="&view.sortDescending.label;" accesskey="&view.sortDescending.accesskey;" - oncommand="ViewMenu.setSortColumn(null, 'descending'); event.stopPropagation();"/> - </menupopup> - </menu> - </menupopup> -#ifdef XP_MACOSX - </toolbarbutton> - <toolbarbutton type="menu" class="tabbable" -#else - </menu> - <menu accesskey="&maintenance.accesskey;" class="menu-iconic" -#endif - id="maintenanceButton" label="&maintenance.label;" - tooltiptext="&maintenance.tooltip;"> - <menupopup id="maintenanceButtonPopup"> - <menuitem id="backupBookmarks" - command="OrganizerCommand_backup" - label="&cmd.backup.label;" - accesskey="&cmd.backup.accesskey;"/> - <menu id="fileRestoreMenu" label="&cmd.restore2.label;" - accesskey="&cmd.restore2.accesskey;"> - <menupopup id="fileRestorePopup" onpopupshowing="PlacesOrganizer.populateRestoreMenu();"> - <menuitem id="restoreFromFile" - command="OrganizerCommand_restoreFromFile" - label="&cmd.restoreFromFile.label;" - accesskey="&cmd.restoreFromFile.accesskey;"/> - </menupopup> - </menu> - <menuseparator/> - <menuitem id="fileImport" - command="OrganizerCommand_import" - label="&importBookmarksFromHTML.label;" - accesskey="&importBookmarksFromHTML.accesskey;"/> - <menuitem id="fileExport" - command="OrganizerCommand_export" - label="&exportBookmarksToHTML.label;" - accesskey="&exportBookmarksToHTML.accesskey;"/> - </menupopup> -#ifdef XP_MACOSX - </toolbarbutton> -#else - </menu> - </menubar> -#endif - - <spacer id="libraryToolbarSpacer" flex="1"/> - - <textbox id="searchFilter" - clickSelectsAll="true" - type="search" - aria-controls="placeContent" - oncommand="PlacesSearchBox.search(this.value);" - collection="bookmarks"> - </textbox> - </toolbar> - </toolbox> - - <hbox flex="1" id="placesView"> - <tree id="placesList" - class="plain placesTree" - type="places" - hidecolumnpicker="true" context="placesContext" - onselect="PlacesOrganizer.onPlaceSelected(true);" - onclick="PlacesOrganizer.onPlacesListClick(event);" - onfocus="PlacesOrganizer.updateDetailsPane(event);" - seltype="single" - persist="width" - width="200" - minwidth="100" - maxwidth="400"> - <treecols> - <treecol anonid="title" flex="1" primary="true" hideheader="true"/> - </treecols> - <treechildren flex="1"/> - </tree> - <splitter collapse="none" persist="state"></splitter> - <vbox id="contentView" flex="4"> - <toolbox id="searchModifiers" hidden="true"> - <toolbar id="organizerScopeBar" class="chromeclass-toolbar" align="center"> - <label id="scopeBarTitle" value="&search.in.label;"/> - <toolbarbutton id="scopeBarAll" class="small-margin" - type="radio" group="scopeBar" - oncommand="PlacesQueryBuilder.onScopeSelected(this);" - label="&search.scopeBookmarks.label;" - accesskey="&search.scopeBookmarks.accesskey;"/> - <toolbarbutton id="scopeBarHistory" class="small-margin" - type="radio" group="scopeBar" - oncommand="PlacesQueryBuilder.onScopeSelected(this);" - label="&search.scopeHistory.label;" - accesskey="&search.scopeHistory.accesskey;"/> - <toolbarbutton id="scopeBarDownloads" class="small-margin" - type="radio" group="scopeBar" - oncommand="PlacesQueryBuilder.onScopeSelected(this);" - label="&search.scopeDownloads.label;" - accesskey="&search.scopeDownloads.accesskey;"/> - <toolbarbutton id="scopeBarFolder" class="small-margin" - type="radio" group="scopeBar" - oncommand="PlacesQueryBuilder.onScopeSelected(this);" - accesskey="&search.scopeFolder.accesskey;" - emptytitle="&search.scopeFolder.label;" flex="1"/> - <!-- The folder scope button should flex but not take up more room - than its label needs. The only simple way to do that is to - set a really big flex on the spacer, e.g., 2^31 - 1. --> - <spacer flex="2147483647"/> - <button id="saveSearch" class="small-margin" - label="&saveSearch.label;" accesskey="&saveSearch.accesskey;" - command="OrganizerCommand_search:save"/> - </toolbar> - </toolbox> - <deck id="placesViewsDeck" - selectedIndex="0" - flex="1"> - <tree id="placeContent" - class="plain placesTree" - context="placesContext" - hidecolumnpicker="true" - flex="1" - type="places" - flatList="true" - selectfirstnode="true" - enableColumnDrag="true" - onfocus="PlacesOrganizer.updateDetailsPane(event)" - onselect="PlacesOrganizer.updateDetailsPane(event)" - onkeypress="ContentTree.onKeyPress(event);" - onopenflatcontainer="PlacesOrganizer.openFlatContainer(aContainer);"> - <treecols id="placeContentColumns" context="placesColumnsContext"> - <treecol label="&col.name.label;" id="placesContentTitle" anonid="title" flex="5" primary="true" ordinal="1" - persist="width hidden ordinal sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol label="&col.tags.label;" id="placesContentTags" anonid="tags" flex="2" - persist="width hidden ordinal sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol label="&col.url.label;" id="placesContentUrl" anonid="url" flex="5" - persist="width hidden ordinal sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol label="&col.lastvisit.label;" id="placesContentDate" anonid="date" flex="1" hidden="true" - persist="width hidden ordinal sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol label="&col.visitcount.label;" id="placesContentVisitCount" anonid="visitCount" flex="1" hidden="true" - persist="width hidden ordinal sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol label="&col.keyword.label;" id="placesContentKeyword" anonid="keyword" flex="1" hidden="true" - persist="width hidden ordinal sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol label="&col.description.label;" id="placesContentDescription" anonid="description" flex="1" hidden="true" - persist="width hidden ordinal sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol label="&col.dateadded.label;" id="placesContentDateAdded" anonid="dateAdded" flex="1" hidden="true" - persist="width hidden ordinal sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol label="&col.lastmodified.label;" id="placesContentLastModified" anonid="lastModified" flex="1" hidden="true" - persist="width hidden ordinal sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol label="&col.parentfolder.label;" id="placesContentParentFolder" anonid="parentFolder" flex="1" hidden="true" - persist="width hidden ordinal"/> - <splitter class="tree-splitter"/> - <treecol label="&col.parentfolderpath.label;" id="placesContentParentFolderPath" anonid="parentFolderPath" flex="1" hidden="true" - persist="width hidden ordinal"/> - </treecols> - <treechildren flex="1" onclick="ContentTree.onClick(event);"/> - </tree> - </deck> - <deck id="detailsDeck" style="height: 11em;"> - <vbox id="itemsCountBox" align="center"> - <spacer flex="3"/> - <label id="itemsCountText"/> - <spacer flex="1"/> - <description id="selectItemDescription"> - &detailsPane.selectAnItemText.description; - </description> - <spacer flex="3"/> - </vbox> - <vbox id="infoBox" minimal="true"> - <vbox id="editBookmarkPanelContent" flex="1"/> - <hbox id="infoBoxExpanderWrapper" align="center"> - - <button type="image" id="infoBoxExpander" - class="expander-down" - oncommand="PlacesOrganizer.toggleAdditionalInfoFields();" - observes="paneElementsBroadcaster"/> - - <label id="infoBoxExpanderLabel" - lesslabel="&detailsPane.less.label;" - lessaccesskey="&detailsPane.less.accesskey;" - morelabel="&detailsPane.more.label;" - moreaccesskey="&detailsPane.more.accesskey;" - value="&detailsPane.more.label;" - accesskey="&detailsPane.more.accesskey;" - control="infoBoxExpander"/> - - </hbox> - </vbox> - </deck> - </vbox> - </hbox> -</window> diff --git a/components/places/content/placesOverlay.xul b/components/places/content/placesOverlay.xul deleted file mode 100644 index 59115a5..0000000 --- a/components/places/content/placesOverlay.xul +++ /dev/null @@ -1,247 +0,0 @@ -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<!DOCTYPE overlay [ -<!ENTITY % placesDTD SYSTEM "chrome://browser/locale/places/places.dtd"> -%placesDTD; -<!ENTITY % editMenuOverlayDTD SYSTEM "chrome://global/locale/editMenuOverlay.dtd"> -%editMenuOverlayDTD; -]> - -<overlay id="placesOverlay" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - - <script type="application/javascript" - src="chrome://global/content/globalOverlay.js"/> - <script type="application/javascript" - src="chrome://browser/content/utilityOverlay.js"/> - <script type="application/javascript"><![CDATA[ - // TODO: Bug 406371. - // A bunch of browser code depends on us defining these, sad but true :( - var Cc = Components.classes; - var Ci = Components.interfaces; - var Cr = Components.results; - - Components.utils.import("resource://gre/modules/PlacesUtils.jsm"); - Components.utils.import("resource:///modules/PlacesUIUtils.jsm"); - ]]></script> - <script type="application/javascript" - src="chrome://browser/content/places/controller.js"/> - <script type="application/javascript" - src="chrome://browser/content/places/treeView.js"/> - - <!-- Bookmarks and history tooltip --> - <tooltip id="bhTooltip" noautohide="true" - onpopupshowing="return window.top.BookmarksEventHandler.fillInBHTooltip(document, event)"> - <vbox id="bhTooltipTextBox" flex="1"> - <label id="bhtTitleText" class="tooltip-label" /> - <label id="bhtUrlText" crop="center" class="tooltip-label" /> - </vbox> - </tooltip> - - <commandset id="placesCommands" - commandupdater="true" - events="focus,sort,places" - oncommandupdate="goUpdatePlacesCommands();"> - <command id="placesCmd_open" - oncommand="goDoPlacesCommand('placesCmd_open');"/> - <command id="placesCmd_open:window" - oncommand="goDoPlacesCommand('placesCmd_open:window');"/> - <command id="placesCmd_open:privatewindow" - oncommand="goDoPlacesCommand('placesCmd_open:privatewindow');"/> - <command id="placesCmd_open:tab" - oncommand="goDoPlacesCommand('placesCmd_open:tab');"/> - - <command id="placesCmd_new:bookmark" - oncommand="goDoPlacesCommand('placesCmd_new:bookmark');"/> - <command id="placesCmd_new:livemark" - oncommand="goDoPlacesCommand('placesCmd_new:livemark');"/> - <command id="placesCmd_new:folder" - oncommand="goDoPlacesCommand('placesCmd_new:folder');"/> - <command id="placesCmd_new:separator" - oncommand="goDoPlacesCommand('placesCmd_new:separator');"/> - <command id="placesCmd_show:info" - oncommand="goDoPlacesCommand('placesCmd_show:info');"/> - <command id="placesCmd_rename" - oncommand="goDoPlacesCommand('placesCmd_show:info');" - observes="placesCmd_show:info"/> - <command id="placesCmd_reload" - oncommand="goDoPlacesCommand('placesCmd_reload');"/> - <command id="placesCmd_sortBy:name" - oncommand="goDoPlacesCommand('placesCmd_sortBy:name');"/> - <command id="placesCmd_moveBookmarks" - oncommand="goDoPlacesCommand('placesCmd_moveBookmarks');"/> - <command id="placesCmd_deleteDataHost" - oncommand="goDoPlacesCommand('placesCmd_deleteDataHost');"/> - <command id="placesCmd_createBookmark" - oncommand="goDoPlacesCommand('placesCmd_createBookmark');"/> - <command id="placesCmd_openParentFolder" - oncommand="goDoPlacesCommand('placesCmd_openParentFolder');"/> - - <!-- Special versions of cut/copy/paste/delete which check for an open context menu. --> - <command id="placesCmd_cut" - oncommand="goDoPlacesCommand('placesCmd_cut');"/> - <command id="placesCmd_copy" - oncommand="goDoPlacesCommand('placesCmd_copy');"/> - <command id="placesCmd_paste" - oncommand="goDoPlacesCommand('placesCmd_paste');"/> - <command id="placesCmd_delete" - oncommand="goDoPlacesCommand('placesCmd_delete');"/> - </commandset> - - <keyset id="placesCommandKeys"> - <key id="key_placesCmd_openParentFolder" - keycode="VK_F1" - command="placesCmd_openParentFolder" - modifiers="accel,shift"/> - </keyset> - - <menupopup id="placesContext" - onpopupshowing="this._view = PlacesUIUtils.getViewForNode(document.popupNode); - return this._view.buildContextMenu(this);" - onpopuphiding="this._view.destroyContextMenu();"> - <menuitem id="placesContext_open" - command="placesCmd_open" - label="&cmd.open.label;" - accesskey="&cmd.open.accesskey;" - default="true" - selectiontype="single" - selection="link"/> - <menuitem id="placesContext_open:newtab" - command="placesCmd_open:tab" - label="&cmd.open_tab.label;" - accesskey="&cmd.open_tab.accesskey;" - selectiontype="single" - selection="link"/> - <menuitem id="placesContext_openContainer:tabs" - oncommand="var view = PlacesUIUtils.getViewForNode(document.popupNode); - view.controller.openSelectionInTabs(event);" - onclick="checkForMiddleClick(this, event);" - label="&cmd.open_all_in_tabs.label;" - accesskey="&cmd.open_all_in_tabs.accesskey;" - selectiontype="single" - selection="folder|host|query"/> - <menuitem id="placesContext_openLinks:tabs" - oncommand="var view = PlacesUIUtils.getViewForNode(document.popupNode); - view.controller.openSelectionInTabs(event);" - onclick="checkForMiddleClick(this, event);" - label="&cmd.open_all_in_tabs.label;" - accesskey="&cmd.open_all_in_tabs.accesskey;" - selectiontype="multiple" - selection="link"/> - <menuitem id="placesContext_open:newwindow" - command="placesCmd_open:window" - label="&cmd.open_window.label;" - accesskey="&cmd.open_window.accesskey;" - selectiontype="single" - selection="link"/> - <menuitem id="placesContext_open:newprivatewindow" - command="placesCmd_open:privatewindow" - label="&cmd.open_private_window.label;" - accesskey="&cmd.open_private_window.accesskey;" - selectiontype="single" - selection="link" - hideifprivatebrowsing="true"/> - <menuseparator id="placesContext_openSeparator"/> - <menuitem id="placesContext_new:bookmark" - command="placesCmd_new:bookmark" - label="&cmd.new_bookmark.label;" - accesskey="&cmd.new_bookmark.accesskey;" - selectiontype="any" - hideifnoinsertionpoint="true"/> - <menuitem id="placesContext_new:folder" - command="placesCmd_new:folder" - label="&cmd.new_folder.label;" - accesskey="&cmd.context_new_folder.accesskey;" - selectiontype="any" - hideifnoinsertionpoint="true"/> - <menuitem id="placesContext_new:separator" - command="placesCmd_new:separator" - label="&cmd.new_separator.label;" - accesskey="&cmd.new_separator.accesskey;" - closemenu="single" - selectiontype="any" - hideifnoinsertionpoint="true"/> - <menuseparator id="placesContext_newSeparator"/> - <menuitem id="placesContext_createBookmark" - command="placesCmd_createBookmark" - label="&cmd.bookmarkLink.label;" - accesskey="&cmd.bookmarkLink.accesskey;" - selection="link" - forcehideselection="bookmark|tagChild"/> - <menuitem id="placesContext_cut" - command="placesCmd_cut" - label="&cutCmd.label;" - accesskey="&cutCmd.accesskey;" - closemenu="single" - selection="bookmark|folder|separator|query" - forcehideselection="tagChild|livemarkChild"/> - <menuitem id="placesContext_copy" - command="placesCmd_copy" - label="©Cmd.label;" - closemenu="single" - accesskey="©Cmd.accesskey;"/> - <menuitem id="placesContext_paste" - command="placesCmd_paste" - label="&pasteCmd.label;" - closemenu="single" - accesskey="&pasteCmd.accesskey;" - selectiontype="any" - hideifnoinsertionpoint="true"/> - <menuseparator id="placesContext_editSeparator"/> - <menuitem id="placesContext_delete" - command="placesCmd_delete" - label="&deleteCmd.label;" - accesskey="&deleteCmd.accesskey;" - closemenu="single" - selection="bookmark|tagChild|folder|query|dynamiccontainer|separator|host"/> - <menuitem id="placesContext_delete_history" - command="placesCmd_delete" - label="&cmd.delete.label;" - accesskey="&cmd.delete.accesskey;" - closemenu="single" - selection="link" - forcehideselection="bookmark|livemarkChild"/> - <menuitem id="placesContext_deleteHost" - command="placesCmd_deleteDataHost" - label="&cmd.deleteDomainData.label;" - accesskey="&cmd.deleteDomainData.accesskey;" - closemenu="single" - selection="link|host" - selectiontype="single" - hideifprivatebrowsing="true" - forcehideselection="bookmark|livemarkChild"/> - <menuseparator id="placesContext_deleteSeparator"/> - <menuitem id="placesContext_reload" - command="placesCmd_reload" - label="&cmd.reloadLivebookmark.label;" - accesskey="&cmd.reloadLivebookmark.accesskey;" - closemenu="single" - selection="livemark/feedURI"/> - <menuitem id="placesContext_sortBy:name" - command="placesCmd_sortBy:name" - label="&cmd.sortby_name.label;" - accesskey="&cmd.context_sortby_name.accesskey;" - closemenu="single" - selection="folder"/> - <menuseparator id="placesContext_sortSeparator"/> - <menuitem id="placesContext_openParentFolder" - command="placesCmd_openParentFolder" - label="&cmd.openParentFolder.label;" - key="key_placesCmd_openParentFolder" - accesskey="&cmd.openParentFolder.accesskey;" - selectiontype="single" - selection="bookmark" - forcehideselection="livemarkChild|livemark/feedURI|PlacesOrganizer/OrganizerQuery"/> - <menuseparator id="placesContext_parentFolderSeparator"/> - <menuitem id="placesContext_show:info" - command="placesCmd_show:info" - label="&cmd.properties.label;" - accesskey="&cmd.properties.accesskey;" - selection="bookmark|folder|query" - forcehideselection="livemarkChild"/> - </menupopup> - -</overlay> diff --git a/components/places/content/sidebarUtils.js b/components/places/content/sidebarUtils.js deleted file mode 100644 index 06ed537..0000000 --- a/components/places/content/sidebarUtils.js +++ /dev/null @@ -1,108 +0,0 @@ -# -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -var SidebarUtils = { - handleTreeClick: function SU_handleTreeClick(aTree, aEvent, aGutterSelect) { - // right-clicks are not handled here - if (aEvent.button == 2) - return; - - var tbo = aTree.treeBoxObject; - var cell = tbo.getCellAt(aEvent.clientX, aEvent.clientY); - - if (cell.row == -1 || cell.childElt == "twisty") - return; - - var mouseInGutter = false; - if (aGutterSelect) { - var rect = tbo.getCoordsForCellItem(cell.row, cell.col, "image"); - // getCoordsForCellItem returns the x coordinate in logical coordinates - // (i.e., starting from the left and right sides in LTR and RTL modes, - // respectively.) Therefore, we make sure to exclude the blank area - // before the tree item icon (that is, to the left or right of it in - // LTR and RTL modes, respectively) from the click target area. - var isRTL = window.getComputedStyle(aTree, null).direction == "rtl"; - if (isRTL) - mouseInGutter = aEvent.clientX > rect.x; - else - mouseInGutter = aEvent.clientX < rect.x; - } - -#ifdef XP_MACOSX - var modifKey = aEvent.metaKey || aEvent.shiftKey; -#else - var modifKey = aEvent.ctrlKey || aEvent.shiftKey; -#endif - - var isContainer = tbo.view.isContainer(cell.row); - var openInTabs = isContainer && - (aEvent.button == 1 || - (aEvent.button == 0 && modifKey)) && - PlacesUtils.hasChildURIs(tbo.view.nodeForTreeIndex(cell.row), true); - - if (aEvent.button == 0 && isContainer && !openInTabs) { - tbo.view.toggleOpenState(cell.row); - return; - } - else if (!mouseInGutter && openInTabs && - aEvent.originalTarget.localName == "treechildren") { - tbo.view.selection.select(cell.row); - PlacesUIUtils.openContainerNodeInTabs(aTree.selectedNode, aEvent, aTree); - } - else if (!mouseInGutter && !isContainer && - aEvent.originalTarget.localName == "treechildren") { - // Clear all other selection since we're loading a link now. We must - // do this *before* attempting to load the link since openURL uses - // selection as an indication of which link to load. - tbo.view.selection.select(cell.row); - PlacesUIUtils.openNodeWithEvent(aTree.selectedNode, aEvent, aTree); - } - }, - - handleTreeKeyPress: function SU_handleTreeKeyPress(aEvent) { - // XXX Bug 627901: Post Fx4, this method should take a tree parameter. - let tree = aEvent.target; - let node = tree.selectedNode; - if (node) { - if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN) - PlacesUIUtils.openNodeWithEvent(node, aEvent, tree); - } - }, - - /** - * The following function displays the URL of a node that is being - * hovered over. - */ - handleTreeMouseMove: function SU_handleTreeMouseMove(aEvent) { - if (aEvent.target.localName != "treechildren") - return; - - var tree = aEvent.target.parentNode; - var tbo = tree.treeBoxObject; - var cell = tbo.getCellAt(aEvent.clientX, aEvent.clientY); - - // cell.row is -1 when the mouse is hovering an empty area within the tree. - // To avoid showing a URL from a previously hovered node for a currently - // hovered non-url node, we must clear the moused-over URL in these cases. - if (cell.row != -1) { - var node = tree.view.nodeForTreeIndex(cell.row); - if (PlacesUtils.nodeIsURI(node)) - this.setMouseoverURL(node.uri); - else - this.setMouseoverURL(""); - } - else - this.setMouseoverURL(""); - }, - - setMouseoverURL: function SU_setMouseoverURL(aURL) { - // When the browser window is closed with an open sidebar, the sidebar - // unload event happens after the browser's one. In this case - // top.XULBrowserWindow has been nullified already. - if (top.XULBrowserWindow) { - top.XULBrowserWindow.setOverLink(aURL, null); - } - } -}; diff --git a/components/places/content/tree.xml b/components/places/content/tree.xml deleted file mode 100644 index 05b0169..0000000 --- a/components/places/content/tree.xml +++ /dev/null @@ -1,789 +0,0 @@ -<?xml version="1.0"?> - -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<bindings id="placesTreeBindings" - xmlns="http://www.mozilla.org/xbl" - xmlns:xbl="http://www.mozilla.org/xbl" - xmlns:html="http://www.w3.org/1999/xhtml" - xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - - <binding id="places-tree" extends="chrome://global/content/bindings/tree.xml#tree"> - <implementation> - <constructor><![CDATA[ - // Force an initial build. - if (this.place) - this.place = this.place; - ]]></constructor> - - <destructor><![CDATA[ - // Break the treeviewer->result->treeviewer cycle. - // Note: unsetting the result's viewer also unsets - // the viewer's reference to our treeBoxObject. - var result = this.result; - if (result) { - result.root.containerOpen = false; - } - - // Unregister the controllber before unlinking the view, otherwise it - // may still try to update commands on a view with a null result. - if (this._controller) { - this._controller.terminate(); - this.controllers.removeController(this._controller); - } - - this.view = null; - ]]></destructor> - - <property name="controller" - readonly="true" - onget="return this._controller"/> - - <!-- overriding --> - <property name="view"> - <getter><![CDATA[ - try { - return this.treeBoxObject.view.wrappedJSObject; - } - catch(e) { - return null; - } - ]]></getter> - <setter><![CDATA[ - return this.treeBoxObject.view = val; - ]]></setter> - </property> - - <property name="associatedElement" - readonly="true" - onget="return this"/> - - <method name="applyFilter"> - <parameter name="filterString"/> - <parameter name="folderRestrict"/> - <parameter name="includeHidden"/> - <body><![CDATA[ - // preserve grouping - var queryNode = PlacesUtils.asQuery(this.result.root); - var options = queryNode.queryOptions.clone(); - - // Make sure we're getting uri results. - // We do not yet support searching into grouped queries or into - // tag containers, so we must fall to the default case. - if (PlacesUtils.nodeIsHistoryContainer(queryNode) || - options.resultType == options.RESULTS_AS_TAG_QUERY || - options.resultType == options.RESULTS_AS_TAG_CONTENTS) - options.resultType = options.RESULTS_AS_URI; - - var query = PlacesUtils.history.getNewQuery(); - query.searchTerms = filterString; - - if (folderRestrict) { - query.setFolders(folderRestrict, folderRestrict.length); - options.queryType = options.QUERY_TYPE_BOOKMARKS; - } - - options.includeHidden = !!includeHidden; - - this.load([query], options); - ]]></body> - </method> - - <method name="load"> - <parameter name="queries"/> - <parameter name="options"/> - <body><![CDATA[ - let result = PlacesUtils.history - .executeQueries(queries, queries.length, - options); - let callback; - if (this.flatList) { - let onOpenFlatContainer = this.onOpenFlatContainer; - if (onOpenFlatContainer) - callback = new Function("aContainer", onOpenFlatContainer); - } - - if (!this._controller) { - this._controller = new PlacesController(this); - this.controllers.appendController(this._controller); - } - - let treeView = new PlacesTreeView(this.flatList, callback, this._controller); - - // Observer removal is done within the view itself. When the tree - // goes away, treeboxobject calls view.setTree(null), which then - // calls removeObserver. - result.addObserver(treeView, false); - this.view = treeView; - - if (this.getAttribute("selectfirstnode") == "true" && treeView.rowCount > 0) { - treeView.selection.select(0); - } - - this._cachedInsertionPoint = undefined; - ]]></body> - </method> - - <property name="flatList"> - <getter><![CDATA[ - return this.getAttribute("flatList") == "true"; - ]]></getter> - <setter><![CDATA[ - if (this.flatList != val) { - this.setAttribute("flatList", val); - // reload with the last place set - if (this.place) - this.place = this.place; - } - return val; - ]]></setter> - </property> - - <property name="onOpenFlatContainer"> - <getter><![CDATA[ - return this.getAttribute("onopenflatcontainer"); - ]]></getter> - <setter><![CDATA[ - if (this.onOpenFlatContainer != val) { - this.setAttribute("onopenflatcontainer", val); - // reload with the last place set - if (this.place) - this.place = this.place; - } - return val; - ]]></setter> - </property> - - <!-- - Causes a particular node represented by the specified placeURI to be - selected in the tree. All containers above the node in the hierarchy - will be opened, so that the node is visible. - --> - <method name="selectPlaceURI"> - <parameter name="placeURI"/> - <body><![CDATA[ - // Do nothing if a node matching the given uri is already selected - if (this.hasSelection && this.selectedNode.uri == placeURI) - return; - - function findNode(container, placeURI, nodesURIChecked) { - var containerURI = container.uri; - if (containerURI == placeURI) - return container; - if (nodesURIChecked.indexOf(containerURI) != -1) - return null; - - // never check the contents of the same query - nodesURIChecked.push(containerURI); - - var wasOpen = container.containerOpen; - if (!wasOpen) - container.containerOpen = true; - for (var i = 0; i < container.childCount; ++i) { - var child = container.getChild(i); - var childURI = child.uri; - if (childURI == placeURI) - return child; - else if (PlacesUtils.nodeIsContainer(child)) { - var nested = findNode(PlacesUtils.asContainer(child), placeURI, nodesURIChecked); - if (nested) - return nested; - } - } - - if (!wasOpen) - container.containerOpen = false; - - return null; - } - - var container = this.result.root; - NS_ASSERT(container, "No result, cannot select place URI!"); - if (!container) - return; - - var child = findNode(container, placeURI, []); - if (child) - this.selectNode(child); - else { - // If the specified child could not be located, clear the selection - var selection = this.view.selection; - selection.clearSelection(); - } - ]]></body> - </method> - - <!-- - Causes a particular node to be selected in the tree, resulting in all - containers above the node in the hierarchy to be opened, so that the - node is visible. - --> - <method name="selectNode"> - <parameter name="node"/> - <body><![CDATA[ - var view = this.view; - - var parent = node.parent; - if (parent && !parent.containerOpen) { - // Build a list of all of the nodes that are the parent of this one - // in the result. - var parents = []; - var root = this.result.root; - while (parent && parent != root) { - parents.push(parent); - parent = parent.parent; - } - - // Walk the list backwards (opening from the root of the hierarchy) - // opening each folder as we go. - for (var i = parents.length - 1; i >= 0; --i) { - var index = view.treeIndexForNode(parents[i]); - if (index != Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE && - view.isContainer(index) && !view.isContainerOpen(index)) - view.toggleOpenState(index); - } - // Select the specified node... - } - - var index = view.treeIndexForNode(node); - if (index == Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE) - return; - - view.selection.select(index); - // ... and ensure it's visible, not scrolled off somewhere. - this.treeBoxObject.ensureRowIsVisible(index); - ]]></body> - </method> - - <!-- nsIPlacesView --> - <property name="result"> - <getter><![CDATA[ - try { - return this.view.QueryInterface(Ci.nsINavHistoryResultObserver).result; - } - catch (e) { - return null; - } - ]]></getter> - </property> - - <!-- nsIPlacesView --> - <property name="place"> - <getter><![CDATA[ - return this.getAttribute("place"); - ]]></getter> - <setter><![CDATA[ - this.setAttribute("place", val); - - var queriesRef = { }; - var queryCountRef = { }; - var optionsRef = { }; - PlacesUtils.history.queryStringToQueries(val, queriesRef, queryCountRef, optionsRef); - if (queryCountRef.value == 0) - queriesRef.value = [PlacesUtils.history.getNewQuery()]; - if (!optionsRef.value) - optionsRef.value = PlacesUtils.history.getNewQueryOptions(); - - this.load(queriesRef.value, optionsRef.value); - - return val; - ]]></setter> - </property> - - <!-- nsIPlacesView --> - <property name="hasSelection"> - <getter><![CDATA[ - return this.view && this.view.selection.count >= 1; - ]]></getter> - </property> - - <!-- nsIPlacesView --> - <property name="selectedNodes"> - <getter><![CDATA[ - let nodes = []; - if (!this.hasSelection) - return nodes; - - let selection = this.view.selection; - let rc = selection.getRangeCount(); - let resultview = this.view; - for (let i = 0; i < rc; ++i) { - let min = { }, max = { }; - selection.getRangeAt(i, min, max); - - for (let j = min.value; j <= max.value; ++j) - nodes.push(resultview.nodeForTreeIndex(j)); - } - return nodes; - ]]></getter> - </property> - - <method name="toggleCutNode"> - <parameter name="aNode"/> - <parameter name="aValue"/> - <body><![CDATA[ - this.view.toggleCutNode(aNode, aValue); - ]]></body> - </method> - - <!-- nsIPlacesView --> - <property name="removableSelectionRanges"> - <getter><![CDATA[ - // This property exists in addition to selectedNodes because it - // encodes selection ranges (which only occur in list views) into - // the return value. For each removed range, the index at which items - // will be re-inserted upon the remove transaction being performed is - // the first index of the range, so that the view updates correctly. - // - // For example, if we remove rows 2,3,4 and 7,8 from a list, when we - // undo that operation, if we insert what was at row 3 at row 3 again, - // it will show up _after_ the item that was at row 5. So we need to - // insert all items at row 2, and the tree view will update correctly. - // - // Also, this function collapses the selection to remove redundant - // data, e.g. when deleting this selection: - // - // http://www.foo.com/ - // (-) Some Folder - // http://www.bar.com/ - // - // ... returning http://www.bar.com/ as part of the selection is - // redundant because it is implied by removing "Some Folder". We - // filter out all such redundancies since some partial amount of - // the folder's children may be selected. - // - let nodes = []; - if (!this.hasSelection) - return nodes; - - var selection = this.view.selection; - var rc = selection.getRangeCount(); - var resultview = this.view; - // This list is kept independently of the range selected (i.e. OUTSIDE - // the for loop) since the row index of a container is unique for the - // entire view, and we could have some really wacky selection and we - // don't want to blow up. - var containers = { }; - for (var i = 0; i < rc; ++i) { - var range = []; - var min = { }, max = { }; - selection.getRangeAt(i, min, max); - - for (var j = min.value; j <= max.value; ++j) { - if (this.view.isContainer(j)) - containers[j] = true; - if (!(this.view.getParentIndex(j) in containers)) - range.push(resultview.nodeForTreeIndex(j)); - } - nodes.push(range); - } - return nodes; - ]]></getter> - </property> - - <!-- nsIPlacesView --> - <property name="draggableSelection" - onget="return this.selectedNodes"/> - - <!-- nsIPlacesView --> - <property name="selectedNode"> - <getter><![CDATA[ - var view = this.view; - if (!view || view.selection.count != 1) - return null; - - var selection = view.selection; - var min = { }, max = { }; - selection.getRangeAt(0, min, max); - - return this.view.nodeForTreeIndex(min.value); - ]]></getter> - </property> - - <!-- nsIPlacesView --> - <property name="insertionPoint"> - <getter><![CDATA[ - // invalidated on selection and focus changes - if (this._cachedInsertionPoint !== undefined) - return this._cachedInsertionPoint; - - // there is no insertion point for history queries - // so bail out now and save a lot of work when updating commands - var resultNode = this.result.root; - if (PlacesUtils.nodeIsQuery(resultNode) && - PlacesUtils.asQuery(resultNode).queryOptions.queryType == - Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) - return this._cachedInsertionPoint = null; - - var orientation = Ci.nsITreeView.DROP_BEFORE; - // If there is no selection, insert at the end of the container. - if (!this.hasSelection) { - var index = this.view.rowCount - 1; - this._cachedInsertionPoint = - this._getInsertionPoint(index, orientation); - return this._cachedInsertionPoint; - } - - // This is a two-part process. The first part is determining the drop - // orientation. - // * The default orientation is to drop _before_ the selected item. - // * If the selected item is a container, the default orientation - // is to drop _into_ that container. - // - // Warning: It may be tempting to use tree indexes in this code, but - // you must not, since the tree is nested and as your tree - // index may change when folders before you are opened and - // closed. You must convert your tree index to a node, and - // then use getChildIndex to find your absolute index in - // the parent container instead. - // - var resultView = this.view; - var selection = resultView.selection; - var rc = selection.getRangeCount(); - var min = { }, max = { }; - selection.getRangeAt(rc - 1, min, max); - - // If the sole selection is a container, and we are not in - // a flatlist, insert into it. - // Note that this only applies to _single_ selections, - // if the last element within a multi-selection is a - // container, insert _adjacent_ to the selection. - // - // If the sole selection is the bookmarks toolbar folder, we insert - // into it even if it is not opened - var itemId = - PlacesUtils.getConcreteItemId(resultView.nodeForTreeIndex(max.value)); - if (selection.count == 1 && resultView.isContainer(max.value) && - !this.flatList) - orientation = Ci.nsITreeView.DROP_ON; - - this._cachedInsertionPoint = - this._getInsertionPoint(max.value, orientation); - return this._cachedInsertionPoint; - ]]></getter> - </property> - - <method name="_getInsertionPoint"> - <parameter name="index"/> - <parameter name="orientation"/> - <body><![CDATA[ - var result = this.result; - var resultview = this.view; - var container = result.root; - var dropNearItemId = -1; - NS_ASSERT(container, "null container"); - // When there's no selection, assume the container is the container - // the view is populated from (i.e. the result's itemId). - if (index != -1) { - var lastSelected = resultview.nodeForTreeIndex(index); - if (resultview.isContainer(index) && orientation == Ci.nsITreeView.DROP_ON) { - // If the last selected item is an open container, append _into_ - // it, rather than insert adjacent to it. - container = lastSelected; - index = -1; - } - else if (lastSelected.containerOpen && - orientation == Ci.nsITreeView.DROP_AFTER && - lastSelected.hasChildren) { - // If the last selected item is an open container and the user is - // trying to drag into it as a first item, really insert into it. - container = lastSelected; - orientation = Ci.nsITreeView.DROP_ON; - index = 0; - } - else { - // Use the last-selected node's container. - container = lastSelected.parent; - - // See comment in the treeView.js's copy of this method - if (!container || !container.containerOpen) - return null; - - // Avoid the potentially expensive call to getChildIndex - // if we know this container doesn't allow insertion - if (PlacesControllerDragHelper.disallowInsertion(container)) - return null; - - var queryOptions = PlacesUtils.asQuery(result.root).queryOptions; - if (queryOptions.sortingMode != - Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) { - // If we are within a sorted view, insert at the end - index = -1; - } - else if (queryOptions.excludeItems || - queryOptions.excludeQueries || - queryOptions.excludeReadOnlyFolders) { - // Some item may be invisible, insert near last selected one. - // We don't replace index here to avoid requests to the db, - // instead it will be calculated later by the controller. - index = -1; - dropNearItemId = lastSelected.itemId; - } - else { - var lsi = container.getChildIndex(lastSelected); - index = orientation == Ci.nsITreeView.DROP_BEFORE ? lsi : lsi + 1; - } - } - } - - if (PlacesControllerDragHelper.disallowInsertion(container)) - return null; - - return new InsertionPoint(PlacesUtils.getConcreteItemId(container), - index, orientation, - PlacesUtils.nodeIsTagQuery(container), - dropNearItemId); - ]]></body> - </method> - - <!-- nsIPlacesView --> - <method name="selectAll"> - <body><![CDATA[ - this.view.selection.selectAll(); - ]]></body> - </method> - - <!-- This method will select the first node in the tree that matches - each given item id. It will open any parent nodes that it needs - to in order to show the selected items. - --> - <method name="selectItems"> - <parameter name="aIDs"/> - <parameter name="aOpenContainers"/> - <body><![CDATA[ - // By default, we do search and select within containers which were - // closed (note that containers in which nodes were not found are - // closed). - if (aOpenContainers === undefined) - aOpenContainers = true; - - var ids = aIDs; // don't manipulate the caller's array - - // Array of nodes found by findNodes which are to be selected - var nodes = []; - - // Array of nodes found by findNodes which should be opened - var nodesToOpen = []; - - // A set of URIs of container-nodes that were previously searched, - // and thus shouldn't be searched again. This is empty at the initial - // start of the recursion and gets filled in as the recursion - // progresses. - var nodesURIChecked = []; - - /** - * Recursively search through a node's children for items - * with the given IDs. When a matching item is found, remove its ID - * from the IDs array, and add the found node to the nodes dictionary. - * - * NOTE: This method will leave open any node that had matching items - * in its subtree. - */ - function findNodes(node) { - var foundOne = false; - // See if node matches an ID we wanted; add to results. - // For simple folder queries, check both itemId and the concrete - // item id. - var index = ids.indexOf(node.itemId); - if (index == -1 && - node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT) - index = ids.indexOf(PlacesUtils.asQuery(node).folderItemId); - - if (index != -1) { - nodes.push(node); - foundOne = true; - ids.splice(index, 1); - } - - if (ids.length == 0 || !PlacesUtils.nodeIsContainer(node) || - nodesURIChecked.indexOf(node.uri) != -1) - return foundOne; - - PlacesUtils.asContainer(node); - if (!aOpenContainers && !node.containerOpen) - return foundOne; - - nodesURIChecked.push(node.uri); - - // Remember the beginning state so that we can re-close - // this node if we don't find any additional results here. - var previousOpenness = node.containerOpen; - node.containerOpen = true; - for (var child = 0; child < node.childCount && ids.length > 0; - child++) { - var childNode = node.getChild(child); - var found = findNodes(childNode); - if (!foundOne) - foundOne = found; - } - - // If we didn't find any additional matches in this node's - // subtree, revert the node to its previous openness. - if (foundOne) - nodesToOpen.unshift(node); - node.containerOpen = previousOpenness; - return foundOne; - } - - // Disable notifications while looking for nodes. - let result = this.result; - let didSuppressNotifications = result.suppressNotifications; - if (!didSuppressNotifications) - result.suppressNotifications = true - try { - findNodes(this.result.root); - } - finally { - if (!didSuppressNotifications) - result.suppressNotifications = false; - } - - // For all the nodes we've found, highlight the corresponding - // index in the tree. - var resultview = this.view; - var selection = this.view.selection; - selection.selectEventsSuppressed = true; - selection.clearSelection(); - // Open nodes containing found items - for (var i = 0; i < nodesToOpen.length; i++) { - nodesToOpen[i].containerOpen = true; - } - for (var i = 0; i < nodes.length; i++) { - var index = resultview.treeIndexForNode(nodes[i]); - if (index == Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE) - continue; - selection.rangedSelect(index, index, true); - } - selection.selectEventsSuppressed = false; - ]]></body> - </method> - - <field name="_contextMenuShown">false</field> - - <method name="buildContextMenu"> - <parameter name="aPopup"/> - <body><![CDATA[ - this._contextMenuShown = true; - return this.controller.buildContextMenu(aPopup); - ]]></body> - </method> - - <method name="destroyContextMenu"> - <parameter name="aPopup"/> - this._contextMenuShown = false; - <body/> - </method> - - <property name="ownerWindow" - readonly="true" - onget="return window;"/> - - <field name="_active">true</field> - <property name="active" - onget="return this._active" - onset="return this._active = val"/> - - </implementation> - <handlers> - <handler event="focus"><![CDATA[ - this._cachedInsertionPoint = undefined; - - // See select handler. We need the sidebar's places commandset to be - // updated as well - document.commandDispatcher.updateCommands("focus"); - ]]></handler> - <handler event="select"><![CDATA[ - this._cachedInsertionPoint = undefined; - - // This additional complexity is here for the sidebars - var win = window; - while (true) { - win.document.commandDispatcher.updateCommands("focus"); - if (win == window.top) - break; - - win = win.parent; - } - ]]></handler> - - <handler event="dragstart"><![CDATA[ - if (event.target.localName != "treechildren") - return; - - let nodes = this.selectedNodes; - for (let i = 0; i < nodes.length; i++) { - let node = nodes[i]; - - // Disallow dragging the root node of a tree. - if (!node.parent) { - event.preventDefault(); - event.stopPropagation(); - return; - } - - // If this node is child of a readonly container (e.g. a livemark) - // or cannot be moved, we must force a copy. - if (!PlacesControllerDragHelper.canMoveNode(node)) { - event.dataTransfer.effectAllowed = "copyLink"; - break; - } - } - - this._controller.setDataTransfer(event); - event.stopPropagation(); - ]]></handler> - - <handler event="dragover"><![CDATA[ - if (event.target.localName != "treechildren") - return; - - let cell = this.treeBoxObject.getCellAt(event.clientX, event.clientY); - let node = cell.row != -1 ? - this.view.nodeForTreeIndex(cell.row) : - this.result.root; - // cache the dropTarget for the view - PlacesControllerDragHelper.currentDropTarget = node; - - // We have to calculate the orientation since view.canDrop will use - // it and we want to be consistent with the dropfeedback. - let tbo = this.treeBoxObject; - let rowHeight = tbo.rowHeight; - let eventY = event.clientY - tbo.treeBody.boxObject.y - - rowHeight * (cell.row - tbo.getFirstVisibleRow()); - - let orientation = Ci.nsITreeView.DROP_BEFORE; - - if (cell.row == -1) { - // If the row is not valid we try to insert inside the resultNode. - orientation = Ci.nsITreeView.DROP_ON; - } - else if (PlacesUtils.nodeIsContainer(node) && - eventY > rowHeight * 0.75) { - // If we are below the 75% of a container the treeview we try - // to drop after the node. - orientation = Ci.nsITreeView.DROP_AFTER; - } - else if (PlacesUtils.nodeIsContainer(node) && - eventY > rowHeight * 0.25) { - // If we are below the 25% of a container the treeview we try - // to drop inside the node. - orientation = Ci.nsITreeView.DROP_ON; - } - - if (!this.view.canDrop(cell.row, orientation, event.dataTransfer)) - return; - - event.preventDefault(); - event.stopPropagation(); - ]]></handler> - - <handler event="dragend"><![CDATA[ - PlacesControllerDragHelper.currentDropTarget = null; - ]]></handler> - - </handlers> - </binding> - -</bindings> diff --git a/components/places/content/treeView.js b/components/places/content/treeView.js deleted file mode 100644 index aba7314..0000000 --- a/components/places/content/treeView.js +++ /dev/null @@ -1,1770 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -Components.utils.import('resource://gre/modules/XPCOMUtils.jsm'); - -const PTV_interfaces = [Ci.nsITreeView, - Ci.nsINavHistoryResultObserver, - Ci.nsINavHistoryResultTreeViewer, - Ci.nsISupportsWeakReference]; - -function PlacesTreeView(aFlatList, aOnOpenFlatContainer, aController) { - this._tree = null; - this._result = null; - this._selection = null; - this._rootNode = null; - this._rows = []; - this._flatList = aFlatList; - this._openContainerCallback = aOnOpenFlatContainer; - this._controller = aController; -} - -PlacesTreeView.prototype = { - get wrappedJSObject() this, - - __dateService: null, - get _dateService() { - if (!this.__dateService) { - this.__dateService = Cc["@mozilla.org/intl/scriptabledateformat;1"]. - getService(Ci.nsIScriptableDateFormat); - } - return this.__dateService; - }, - - QueryInterface: XPCOMUtils.generateQI(PTV_interfaces), - - // Bug 761494: - // ---------- - // Some addons use methods from nsINavHistoryResultObserver and - // nsINavHistoryResultTreeViewer, without QIing to these interfaces first. - // That's not a problem when the view is retrieved through the - // <tree>.view getter (which returns the wrappedJSObject of this object), - // it raises an issue when the view retrieved through the treeBoxObject.view - // getter. Thus, to avoid breaking addons, the interfaces are prefetched. - classInfo: XPCOMUtils.generateCI({ interfaces: PTV_interfaces }), - - /** - * This is called once both the result and the tree are set. - */ - _finishInit: function PTV__finishInit() { - let selection = this.selection; - if (selection) - selection.selectEventsSuppressed = true; - - if (!this._rootNode.containerOpen) { - // This triggers containerStateChanged which then builds the visible - // section. - this._rootNode.containerOpen = true; - } - else - this.invalidateContainer(this._rootNode); - - // "Activate" the sorting column and update commands. - this.sortingChanged(this._result.sortingMode); - - if (selection) - selection.selectEventsSuppressed = false; - }, - - /** - * Plain Container: container result nodes which may never include sub - * hierarchies. - * - * When the rows array is constructed, we don't set the children of plain - * containers. Instead, we keep placeholders for these children. We then - * build these children lazily as the tree asks us for information about each - * row. Luckily, the tree doesn't ask about rows outside the visible area. - * - * @see _getNodeForRow and _getRowForNode for the actual magic. - * - * @note It's guaranteed that all containers are listed in the rows - * elements array. It's also guaranteed that separators (if they're not - * filtered, see below) are listed in the visible elements array, because - * bookmark folders are never built lazily, as described above. - * - * @param aContainer - * A container result node. - * - * @return true if aContainer is a plain container, false otherwise. - */ - _isPlainContainer: function PTV__isPlainContainer(aContainer) { - // Livemarks are always plain containers. - if (this._controller.hasCachedLivemarkInfo(aContainer)) - return true; - - // We don't know enough about non-query containers. - if (!(aContainer instanceof Ci.nsINavHistoryQueryResultNode)) - return false; - - switch (aContainer.queryOptions.resultType) { - case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY: - case Ci.nsINavHistoryQueryOptions.RESULTS_AS_SITE_QUERY: - case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_SITE_QUERY: - case Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY: - return false; - } - - // If it's a folder, it's not a plain container. - let nodeType = aContainer.type; - return nodeType != Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER && - nodeType != Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT; - }, - - /** - * Gets the row number for a given node. Assumes that the given node is - * visible (i.e. it's not an obsolete node). - * - * @param aNode - * A result node. Do not pass an obsolete node, or any - * node which isn't supposed to be in the tree (e.g. separators in - * sorted trees). - * @param [optional] aForceBuild - * @see _isPlainContainer. - * If true, the row will be computed even if the node still isn't set - * in our rows array. - * @param [optional] aParentRow - * The row of aNode's parent. Ignored for the root node. - * @param [optional] aNodeIndex - * The index of aNode in its parent. Only used if aParentRow is - * set too. - * - * @throws if aNode is invisible. - * @note If aParentRow and aNodeIndex are passed and parent is a plain - * container, this method will just return a calculated row value, without - * making assumptions on existence of the node at that position. - * @return aNode's row if it's in the rows list or if aForceBuild is set, -1 - * otherwise. - */ - _getRowForNode: - function PTV__getRowForNode(aNode, aForceBuild, aParentRow, aNodeIndex) { - if (aNode == this._rootNode) - throw new Error("The root node is never visible"); - - // A node is removed form the view either if it has no parent or if its - // root-ancestor is not the root node (in which case that's the node - // for which nodeRemoved was called). - // Tycho: let ancestors = [x for (x of PlacesUtils.nodeAncestors(aNode))]; - let ancestors = []; - for (let x of PlacesUtils.nodeAncestors(aNode)) { - ancestors.push(x); - } - - if (ancestors.length == 0 || - ancestors[ancestors.length - 1] != this._rootNode) { - throw new Error("Removed node passed to _getRowForNode"); - } - - // Ensure that the entire chain is open, otherwise that node is invisible. - for (let ancestor of ancestors) { - if (!ancestor.containerOpen) - throw new Error("Invisible node passed to _getRowForNode"); - } - - // Non-plain containers are initially built with their contents. - let parent = aNode.parent; - let parentIsPlain = this._isPlainContainer(parent); - if (!parentIsPlain) { - if (parent == this._rootNode) - return this._rows.indexOf(aNode); - - return this._rows.indexOf(aNode, aParentRow); - } - - let row = -1; - let useNodeIndex = typeof(aNodeIndex) == "number"; - if (parent == this._rootNode) { - if (aNode instanceof Ci.nsINavHistoryResultNode) { - row = useNodeIndex ? aNodeIndex : this._rootNode.getChildIndex(aNode); - } - } else if (useNodeIndex && typeof(aParentRow) == "number") { - // If we have both the row of the parent node, and the node's index, we - // can avoid searching the rows array if the parent is a plain container. - row = aParentRow + aNodeIndex + 1; - } else { - // Look for the node in the nodes array. Start the search at the parent - // row. If the parent row isn't passed, we'll pass undefined to indexOf, - // which is fine. - row = this._rows.indexOf(aNode, aParentRow); - if (row == -1 && aForceBuild) { - let parentRow = typeof(aParentRow) == "number" ? aParentRow - : this._getRowForNode(parent); - row = parentRow + parent.getChildIndex(aNode) + 1; - } - } - - if (row != -1) - this._rows[row] = aNode; - - return row; - }, - - /** - * Given a row, finds and returns the parent details of the associated node. - * - * @param aChildRow - * Row number. - * @return [parentNode, parentRow] - */ - _getParentByChildRow: function PTV__getParentByChildRow(aChildRow) { - let node = this._getNodeForRow(aChildRow); - let parent = (node === null) ? this._rootNode : node.parent; - - // The root node is never visible - if (parent == this._rootNode) - return [this._rootNode, -1]; - - let parentRow = this._rows.lastIndexOf(parent, aChildRow - 1); - return [parent, parentRow]; - }, - - /** - * Gets the node at a given row. - */ - _getNodeForRow: function PTV__getNodeForRow(aRow) { - if (aRow < 0) { - return null; - } - - let node = this._rows[aRow]; - if (node !== undefined) - return node; - - // Find the nearest node. - let rowNode, row; - for (let i = aRow - 1; i >= 0 && rowNode === undefined; i--) { - rowNode = this._rows[i]; - row = i; - } - - // If there's no container prior to the given row, it's a child of - // the root node (remember: all containers are listed in the rows array). - if (!rowNode) - return this._rows[aRow] = this._rootNode.getChild(aRow); - - // Unset elements may exist only in plain containers. Thus, if the nearest - // node is a container, it's the row's parent, otherwise, it's a sibling. - if (rowNode instanceof Ci.nsINavHistoryContainerResultNode) - return this._rows[aRow] = rowNode.getChild(aRow - row - 1); - - let [parent, parentRow] = this._getParentByChildRow(row); - return this._rows[aRow] = parent.getChild(aRow - parentRow - 1); - }, - - /** - * This takes a container and recursively appends our rows array per its - * contents. Assumes that the rows arrays has no rows for the given - * container. - * - * @param [in] aContainer - * A container result node. - * @param [in] aFirstChildRow - * The first row at which nodes may be inserted to the row array. - * In other words, that's aContainer's row + 1. - * @param [out] aToOpen - * An array of containers to open once the build is done. - * - * @return the number of rows which were inserted. - */ - _buildVisibleSection: - function PTV__buildVisibleSection(aContainer, aFirstChildRow, aToOpen) - { - // There's nothing to do if the container is closed. - if (!aContainer.containerOpen) - return 0; - - // Inserting the new elements into the rows array in one shot (by - // Array.concat) is faster than resizing the array (by splice) on each loop - // iteration. - let cc = aContainer.childCount; - let newElements = new Array(cc); - this._rows = this._rows.splice(0, aFirstChildRow) - .concat(newElements, this._rows); - - if (this._isPlainContainer(aContainer)) - return cc; - - const openLiteral = PlacesUIUtils.RDF.GetResource("http://home.netscape.com/NC-rdf#open"); - const trueLiteral = PlacesUIUtils.RDF.GetLiteral("true"); - let sortingMode = this._result.sortingMode; - - let rowsInserted = 0; - for (let i = 0; i < cc; i++) { - let curChild = aContainer.getChild(i); - let curChildType = curChild.type; - - let row = aFirstChildRow + rowsInserted; - - // Don't display separators when sorted. - if (curChildType == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) { - if (sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) { - // Remove the element for the filtered separator. - // Notice that the rows array was initially resized to include all - // children. - this._rows.splice(row, 1); - continue; - } - } - - this._rows[row] = curChild; - rowsInserted++; - - // Recursively do containers. - if (!this._flatList && - curChild instanceof Ci.nsINavHistoryContainerResultNode && - !this._controller.hasCachedLivemarkInfo(curChild)) { - let resource = this._getResourceForNode(curChild); - let isopen = resource != null && - PlacesUIUtils.localStore.HasAssertion(resource, - openLiteral, - trueLiteral, true); - if (isopen != curChild.containerOpen) - aToOpen.push(curChild); - else if (curChild.containerOpen && curChild.childCount > 0) - rowsInserted += this._buildVisibleSection(curChild, row + 1, aToOpen); - } - } - - return rowsInserted; - }, - - /** - * This counts how many rows a node takes in the tree. For containers it - * will count the node itself plus any child node following it. - */ - _countVisibleRowsForNodeAtRow: - function PTV__countVisibleRowsForNodeAtRow(aNodeRow) { - let node = this._rows[aNodeRow]; - - // If it's not listed yet, we know that it's a leaf node (instanceof also - // null-checks). - if (!(node instanceof Ci.nsINavHistoryContainerResultNode)) - return 1; - - let outerLevel = node.indentLevel; - for (let i = aNodeRow + 1; i < this._rows.length; i++) { - let rowNode = this._rows[i]; - if (rowNode && rowNode.indentLevel <= outerLevel) - return i - aNodeRow; - } - - // This node plus its children take up the bottom of the list. - return this._rows.length - aNodeRow; - }, - - _getSelectedNodesInRange: - function PTV__getSelectedNodesInRange(aFirstRow, aLastRow) { - let selection = this.selection; - let rc = selection.getRangeCount(); - if (rc == 0) - return []; - - // The visible-area borders are needed for checking whether a - // selected row is also visible. - let firstVisibleRow = this._tree.getFirstVisibleRow(); - let lastVisibleRow = this._tree.getLastVisibleRow(); - - let nodesInfo = []; - for (let rangeIndex = 0; rangeIndex < rc; rangeIndex++) { - let min = { }, max = { }; - selection.getRangeAt(rangeIndex, min, max); - - // If this range does not overlap the replaced chunk, we don't need to - // persist the selection. - if (max.value < aFirstRow || min.value > aLastRow) - continue; - - let firstRow = Math.max(min.value, aFirstRow); - let lastRow = Math.min(max.value, aLastRow); - for (let i = firstRow; i <= lastRow; i++) { - nodesInfo.push({ - node: this._rows[i], - oldRow: i, - wasVisible: i >= firstVisibleRow && i <= lastVisibleRow - }); - } - } - - return nodesInfo; - }, - - /** - * Tries to find an equivalent node for a node which was removed. We first - * look for the original node, in case it was just relocated. Then, if we - * that node was not found, we look for a node that has the same itemId, uri - * and time values. - * - * @param aUpdatedContainer - * An ancestor of the node which was removed. It does not have to be - * its direct parent. - * @param aOldNode - * The node which was removed. - * - * @return the row number of an equivalent node for aOldOne, if one was - * found, -1 otherwise. - */ - _getNewRowForRemovedNode: - function PTV__getNewRowForRemovedNode(aUpdatedContainer, aOldNode) { - if (aOldNode == undefined) { - return -1; - } - let parent = aOldNode.parent; - if (parent) { - // If the node's parent is still set, the node is not obsolete - // and we should just find out its new position. - // However, if any of the node's ancestor is closed, the node is - // invisible. - let ancestors = PlacesUtils.nodeAncestors(aOldNode); - for (let ancestor of ancestors) { - if (!ancestor.containerOpen) - return -1; - } - - return this._getRowForNode(aOldNode, true); - } - - // There's a broken edge case here. - // If a visit appears in two queries, and the second one was - // the old node, we'll select the first one after refresh. There's - // nothing we could do about that, because aOldNode.parent is - // gone by the time invalidateContainer is called. - let newNode = aUpdatedContainer.findNodeByDetails(aOldNode.uri, - aOldNode.time, - aOldNode.itemId, - true); - if (!newNode) - return -1; - - return this._getRowForNode(newNode, true); - }, - - /** - * Restores a given selection state as near as possible to the original - * selection state. - * - * @param aNodesInfo - * The persisted selection state as returned by - * _getSelectedNodesInRange. - * @param aUpdatedContainer - * The container which was updated. - */ - _restoreSelection: - function PTV__restoreSelection(aNodesInfo, aUpdatedContainer) { - if (aNodesInfo.length == 0) - return; - - let selection = this.selection; - - // Attempt to ensure that previously-visible selection will be visible - // if it's re-selected. However, we can only ensure that for one row. - let scrollToRow = -1; - for (let i = 0; i < aNodesInfo.length; i++) { - let nodeInfo = aNodesInfo[i]; - let row = this._getNewRowForRemovedNode(aUpdatedContainer, - nodeInfo.node); - // Select the found node, if any. - if (row != -1) { - selection.rangedSelect(row, row, true); - if (nodeInfo.wasVisible && scrollToRow == -1) - scrollToRow = row; - } - } - - // If only one node was previously selected and there's no selection now, - // select the node at its old row, if any. - if (aNodesInfo.length == 1 && selection.count == 0) { - let row = Math.min(aNodesInfo[0].oldRow, this._rows.length - 1); - if (row != -1) { - selection.rangedSelect(row, row, true); - if (aNodesInfo[0].wasVisible && scrollToRow == -1) - scrollToRow = aNodesInfo[0].oldRow; - } - } - - if (scrollToRow != -1) - this._tree.ensureRowIsVisible(scrollToRow); - }, - - _convertPRTimeToString: function PTV__convertPRTimeToString(aTime) { - const MS_PER_MINUTE = 60000; - const MS_PER_DAY = 86400000; - let timeMs = aTime / 1000; // PRTime is in microseconds - - // Date is calculated starting from midnight, so the modulo with a day are - // milliseconds from today's midnight. - // getTimezoneOffset corrects that based on local time, notice midnight - // can have a different offset during DST-change days. - let dateObj = new Date(); - let now = dateObj.getTime() - dateObj.getTimezoneOffset() * MS_PER_MINUTE; - let midnight = now - (now % MS_PER_DAY); - midnight += new Date(midnight).getTimezoneOffset() * MS_PER_MINUTE; - - let dateFormat = timeMs >= midnight ? - Ci.nsIScriptableDateFormat.dateFormatNone : - Ci.nsIScriptableDateFormat.dateFormatShort; - - let timeObj = new Date(timeMs); - return (this._dateService.FormatDateTime("", dateFormat, - Ci.nsIScriptableDateFormat.timeFormatNoSeconds, - timeObj.getFullYear(), timeObj.getMonth() + 1, - timeObj.getDate(), timeObj.getHours(), - timeObj.getMinutes(), timeObj.getSeconds())); - }, - - COLUMN_TYPE_UNKNOWN: 0, - COLUMN_TYPE_TITLE: 1, - COLUMN_TYPE_URI: 2, - COLUMN_TYPE_DATE: 3, - COLUMN_TYPE_VISITCOUNT: 4, - COLUMN_TYPE_KEYWORD: 5, - COLUMN_TYPE_DESCRIPTION: 6, - COLUMN_TYPE_DATEADDED: 7, - COLUMN_TYPE_LASTMODIFIED: 8, - COLUMN_TYPE_TAGS: 9, - COLUMN_TYPE_PARENTFOLDER: 10, - COLUMN_TYPE_PARENTFOLDERPATH: 11, - - _getColumnType: function PTV__getColumnType(aColumn) { - let columnType = aColumn.element.getAttribute("anonid") || aColumn.id; - - switch (columnType) { - case "title": - return this.COLUMN_TYPE_TITLE; - case "url": - return this.COLUMN_TYPE_URI; - case "date": - return this.COLUMN_TYPE_DATE; - case "visitCount": - return this.COLUMN_TYPE_VISITCOUNT; - case "keyword": - return this.COLUMN_TYPE_KEYWORD; - case "description": - return this.COLUMN_TYPE_DESCRIPTION; - case "dateAdded": - return this.COLUMN_TYPE_DATEADDED; - case "lastModified": - return this.COLUMN_TYPE_LASTMODIFIED; - case "tags": - return this.COLUMN_TYPE_TAGS; - case "parentFolder": - return this.COLUMN_TYPE_PARENTFOLDER; - case "parentFolderPath": - return this.COLUMN_TYPE_PARENTFOLDERPATH; - } - return this.COLUMN_TYPE_UNKNOWN; - }, - - _sortTypeToColumnType: function PTV__sortTypeToColumnType(aSortType) { - switch (aSortType) { - case Ci.nsINavHistoryQueryOptions.SORT_BY_TITLE_ASCENDING: - return [this.COLUMN_TYPE_TITLE, false]; - case Ci.nsINavHistoryQueryOptions.SORT_BY_TITLE_DESCENDING: - return [this.COLUMN_TYPE_TITLE, true]; - case Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_ASCENDING: - return [this.COLUMN_TYPE_DATE, false]; - case Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING: - return [this.COLUMN_TYPE_DATE, true]; - case Ci.nsINavHistoryQueryOptions.SORT_BY_URI_ASCENDING: - return [this.COLUMN_TYPE_URI, false]; - case Ci.nsINavHistoryQueryOptions.SORT_BY_URI_DESCENDING: - return [this.COLUMN_TYPE_URI, true]; - case Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_ASCENDING: - return [this.COLUMN_TYPE_VISITCOUNT, false]; - case Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_DESCENDING: - return [this.COLUMN_TYPE_VISITCOUNT, true]; - case Ci.nsINavHistoryQueryOptions.SORT_BY_KEYWORD_ASCENDING: - return [this.COLUMN_TYPE_KEYWORD, false]; - case Ci.nsINavHistoryQueryOptions.SORT_BY_KEYWORD_DESCENDING: - return [this.COLUMN_TYPE_KEYWORD, true]; - case Ci.nsINavHistoryQueryOptions.SORT_BY_ANNOTATION_ASCENDING: - if (this._result.sortingAnnotation == PlacesUIUtils.DESCRIPTION_ANNO) - return [this.COLUMN_TYPE_DESCRIPTION, false]; - break; - case Ci.nsINavHistoryQueryOptions.SORT_BY_ANNOTATION_DESCENDING: - if (this._result.sortingAnnotation == PlacesUIUtils.DESCRIPTION_ANNO) - return [this.COLUMN_TYPE_DESCRIPTION, true]; - case Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_ASCENDING: - return [this.COLUMN_TYPE_DATEADDED, false]; - case Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_DESCENDING: - return [this.COLUMN_TYPE_DATEADDED, true]; - case Ci.nsINavHistoryQueryOptions.SORT_BY_LASTMODIFIED_ASCENDING: - return [this.COLUMN_TYPE_LASTMODIFIED, false]; - case Ci.nsINavHistoryQueryOptions.SORT_BY_LASTMODIFIED_DESCENDING: - return [this.COLUMN_TYPE_LASTMODIFIED, true]; - case Ci.nsINavHistoryQueryOptions.SORT_BY_TAGS_ASCENDING: - return [this.COLUMN_TYPE_TAGS, false]; - case Ci.nsINavHistoryQueryOptions.SORT_BY_TAGS_DESCENDING: - return [this.COLUMN_TYPE_TAGS, true]; - } - return [this.COLUMN_TYPE_UNKNOWN, false]; - }, - - // nsINavHistoryResultObserver - nodeInserted: function PTV_nodeInserted(aParentNode, aNode, aNewIndex) { - NS_ASSERT(this._result, "Got a notification but have no result!"); - if (!this._tree || !this._result) - return; - - // Bail out for hidden separators. - if (PlacesUtils.nodeIsSeparator(aNode) && this.isSorted()) - return; - - let parentRow; - if (aParentNode != this._rootNode) { - parentRow = this._getRowForNode(aParentNode); - - // Update parent when inserting the first item, since twisty has changed. - if (aParentNode.childCount == 1) - this._tree.invalidateRow(parentRow); - } - - // Compute the new row number of the node. - let row = -1; - let cc = aParentNode.childCount; - if (aNewIndex == 0 || this._isPlainContainer(aParentNode) || cc == 0) { - // We don't need to worry about sub hierarchies of the parent node - // if it's a plain container, or if the new node is its first child. - if (aParentNode == this._rootNode) - row = aNewIndex; - else - row = parentRow + aNewIndex + 1; - } - else { - // Here, we try to find the next visible element in the child list so we - // can set the new visible index to be right before that. Note that we - // have to search down instead of up, because some siblings could have - // children themselves that would be in the way. - let separatorsAreHidden = PlacesUtils.nodeIsSeparator(aNode) && - this.isSorted(); - for (let i = aNewIndex + 1; i < cc; i++) { - let node = aParentNode.getChild(i); - if (!separatorsAreHidden || PlacesUtils.nodeIsSeparator(node)) { - // The children have not been shifted so the next item will have what - // should be our index. - row = this._getRowForNode(node, false, parentRow, i); - break; - } - } - if (row < 0) { - // At the end of the child list without finding a visible sibling. This - // is a little harder because we don't know how many rows the last item - // in our list takes up (it could be a container with many children). - let prevChild = aParentNode.getChild(aNewIndex - 1); - let prevIndex = this._getRowForNode(prevChild, false, parentRow, - aNewIndex - 1); - row = prevIndex + this._countVisibleRowsForNodeAtRow(prevIndex); - } - } - - this._rows.splice(row, 0, aNode); - this._tree.rowCountChanged(row, 1); - - if (PlacesUtils.nodeIsContainer(aNode) && - PlacesUtils.asContainer(aNode).containerOpen) { - this.invalidateContainer(aNode); - } - }, - - /** - * THIS FUNCTION DOES NOT HANDLE cases where a collapsed node is being - * removed but the node it is collapsed with is not being removed (this then - * just swap out the removee with its collapsing partner). The only time - * when we really remove things is when deleting URIs, which will apply to - * all collapsees. This function is called sometimes when resorting items. - * However, we won't do this when sorted by date because dates will never - * change for visits, and date sorting is the only time things are collapsed. - */ - nodeRemoved: function PTV_nodeRemoved(aParentNode, aNode, aOldIndex) { - NS_ASSERT(this._result, "Got a notification but have no result!"); - if (!this._tree || !this._result) - return; - - // XXX bug 517701: We don't know what to do when the root node is removed. - if (aNode == this._rootNode) - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - - // Bail out for hidden separators. - if (PlacesUtils.nodeIsSeparator(aNode) && this.isSorted()) - return; - - let parentRow = aParentNode == this._rootNode ? - undefined : this._getRowForNode(aParentNode, true); - let oldRow = this._getRowForNode(aNode, true, parentRow, aOldIndex); - if (oldRow < 0) - throw Cr.NS_ERROR_UNEXPECTED; - - // If the node was exclusively selected, the node next to it will be - // selected. - let selectNext = false; - let selection = this.selection; - if (selection.getRangeCount() == 1) { - let min = { }, max = { }; - selection.getRangeAt(0, min, max); - if (min.value == max.value && - this.nodeForTreeIndex(min.value) == aNode) - selectNext = true; - } - - // Remove the node and its children, if any. - let count = this._countVisibleRowsForNodeAtRow(oldRow); - this._rows.splice(oldRow, count); - this._tree.rowCountChanged(oldRow, -count); - - // Redraw the parent if its twisty state has changed. - if (aParentNode != this._rootNode && !aParentNode.hasChildren) { - let parentRow = oldRow - 1; - this._tree.invalidateRow(parentRow); - } - - // Restore selection if the node was exclusively selected. - if (!selectNext) - return; - - // Restore selection. - let rowToSelect = Math.min(oldRow, this._rows.length - 1); - if (rowToSelect != -1) - this.selection.rangedSelect(rowToSelect, rowToSelect, true); - }, - - nodeMoved: - function PTV_nodeMoved(aNode, aOldParent, aOldIndex, aNewParent, aNewIndex) { - NS_ASSERT(this._result, "Got a notification but have no result!"); - if (!this._tree || !this._result) - return; - - // Bail out for hidden separators. - if (PlacesUtils.nodeIsSeparator(aNode) && this.isSorted()) - return; - - // Note that at this point the node has already been moved by the backend, - // so we must give hints to _getRowForNode to get the old row position. - let oldParentRow = aOldParent == this._rootNode ? - undefined : this._getRowForNode(aOldParent, true); - let oldRow = this._getRowForNode(aNode, true, oldParentRow, aOldIndex); - if (oldRow < 0) - throw Cr.NS_ERROR_UNEXPECTED; - - // If this node is a container it could take up more than one row. - let count = this._countVisibleRowsForNodeAtRow(oldRow); - - // Persist selection state. - let nodesToReselect = - this._getSelectedNodesInRange(oldRow, oldRow + count); - if (nodesToReselect.length > 0) - this.selection.selectEventsSuppressed = true; - - // Redraw the parent if its twisty state has changed. - if (aOldParent != this._rootNode && !aOldParent.hasChildren) { - let parentRow = oldRow - 1; - this._tree.invalidateRow(parentRow); - } - - // Remove node and its children, if any, from the old position. - this._rows.splice(oldRow, count); - this._tree.rowCountChanged(oldRow, -count); - - // Insert the node into the new position. - this.nodeInserted(aNewParent, aNode, aNewIndex); - - // Restore selection. - if (nodesToReselect.length > 0) { - this._restoreSelection(nodesToReselect, aNewParent); - this.selection.selectEventsSuppressed = false; - } - }, - - _invalidateCellValue: function PTV__invalidateCellValue(aNode, - aColumnType) { - NS_ASSERT(this._result, "Got a notification but have no result!"); - if (!this._tree || !this._result) - return; - - // Nothing to do for the root node. - if (aNode == this._rootNode) - return; - - let row = this._getRowForNode(aNode); - if (row == -1) - return; - - let column = this._findColumnByType(aColumnType); - if (column && !column.element.hidden) - this._tree.invalidateCell(row, column); - - // Last modified time is altered for almost all node changes. - if (aColumnType != this.COLUMN_TYPE_LASTMODIFIED) { - let lastModifiedColumn = - this._findColumnByType(this.COLUMN_TYPE_LASTMODIFIED); - if (lastModifiedColumn && !lastModifiedColumn.hidden) - this._tree.invalidateCell(row, lastModifiedColumn); - } - }, - - _populateLivemarkContainer: function PTV__populateLivemarkContainer(aNode) { - PlacesUtils.livemarks.getLivemark({ id: aNode.itemId }) - .then(aLivemark => { - let placesNode = aNode; - // Need to check containerOpen since getLivemark is async. - if (!placesNode.containerOpen) - return; - - let children = aLivemark.getNodesForContainer(placesNode); - for (let i = 0; i < children.length; i++) { - let child = children[i]; - this.nodeInserted(placesNode, child, i); - } - }, Components.utils.reportError); - }, - - nodeTitleChanged: function PTV_nodeTitleChanged(aNode, aNewTitle) { - this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE); - }, - - nodeURIChanged: function PTV_nodeURIChanged(aNode, aNewURI) { - this._invalidateCellValue(aNode, this.COLUMN_TYPE_URI); - }, - - nodeIconChanged: function PTV_nodeIconChanged(aNode) { - this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE); - }, - - nodeHistoryDetailsChanged: - function PTV_nodeHistoryDetailsChanged(aNode, aUpdatedVisitDate, - aUpdatedVisitCount) { - if (aNode.parent && this._controller.hasCachedLivemarkInfo(aNode.parent)) { - // Find the node in the parent. - let parentRow = this._flatList ? 0 : this._getRowForNode(aNode.parent); - for (let i = parentRow; i < this._rows.length; i++) { - let child = this.nodeForTreeIndex(i); - if (child.uri == aNode.uri) { - this._cellProperties.delete(child); - this._invalidateCellValue(child, this.COLUMN_TYPE_TITLE); - break; - } - } - return; - } - - this._invalidateCellValue(aNode, this.COLUMN_TYPE_DATE); - this._invalidateCellValue(aNode, this.COLUMN_TYPE_VISITCOUNT); - }, - - nodeTagsChanged: function PTV_nodeTagsChanged(aNode) { - this._invalidateCellValue(aNode, this.COLUMN_TYPE_TAGS); - }, - - nodeKeywordChanged: function PTV_nodeKeywordChanged(aNode, aNewKeyword) { - this._invalidateCellValue(aNode, this.COLUMN_TYPE_KEYWORD); - }, - - nodeAnnotationChanged: function PTV_nodeAnnotationChanged(aNode, aAnno) { - if (aAnno == PlacesUIUtils.DESCRIPTION_ANNO) { - this._invalidateCellValue(aNode, this.COLUMN_TYPE_DESCRIPTION); - } - else if (aAnno == PlacesUtils.LMANNO_FEEDURI) { - PlacesUtils.livemarks.getLivemark({ id: aNode.itemId }) - .then(aLivemark => { - this._controller.cacheLivemarkInfo(aNode, aLivemark); - let properties = this._cellProperties.get(aNode); - this._cellProperties.set(aNode, properties += " livemark"); - // The livemark attribute is set as a cell property on the title cell. - this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE); - }, Components.utils.reportError); - } - }, - - nodeDateAddedChanged: function PTV_nodeDateAddedChanged(aNode, aNewValue) { - this._invalidateCellValue(aNode, this.COLUMN_TYPE_DATEADDED); - }, - - nodeLastModifiedChanged: - function PTV_nodeLastModifiedChanged(aNode, aNewValue) { - this._invalidateCellValue(aNode, this.COLUMN_TYPE_LASTMODIFIED); - }, - - containerStateChanged: - function PTV_containerStateChanged(aNode, aOldState, aNewState) { - this.invalidateContainer(aNode); - - if (PlacesUtils.nodeIsFolder(aNode) || - (this._flatList && aNode == this._rootNode)) { - let queryOptions = PlacesUtils.asQuery(this._rootNode).queryOptions; - if (queryOptions.excludeItems) { - return; - } - if (aNode.itemId != -1) { // run when there's a valid node id - PlacesUtils.livemarks.getLivemark({ id: aNode.itemId }) - .then(aLivemark => { - let shouldInvalidate = - !this._controller.hasCachedLivemarkInfo(aNode); - this._controller.cacheLivemarkInfo(aNode, aLivemark); - if (aNewState == Components.interfaces.nsINavHistoryContainerResultNode.STATE_OPENED) { - aLivemark.registerForUpdates(aNode, this); - // Prioritize the current livemark. - aLivemark.reload(); - PlacesUtils.livemarks.reloadLivemarks(); - if (shouldInvalidate) - this.invalidateContainer(aNode); - } - else { - aLivemark.unregisterForUpdates(aNode); - } - }, () => undefined); - } - } - }, - - invalidateContainer: function PTV_invalidateContainer(aContainer) { - NS_ASSERT(this._result, "Need to have a result to update"); - if (!this._tree) - return; - - let startReplacement, replaceCount; - if (aContainer == this._rootNode) { - startReplacement = 0; - replaceCount = this._rows.length; - - // If the root node is now closed, the tree is empty. - if (!this._rootNode.containerOpen) { - this._rows = []; - if (replaceCount) - this._tree.rowCountChanged(startReplacement, -replaceCount); - - return; - } - } - else { - // Update the twisty state. - let row = this._getRowForNode(aContainer); - this._tree.invalidateRow(row); - - // We don't replace the container node itself, so we should decrease the - // replaceCount by 1. - startReplacement = row + 1; - replaceCount = this._countVisibleRowsForNodeAtRow(row) - 1; - } - - // Persist selection state. - let nodesToReselect = - this._getSelectedNodesInRange(startReplacement, - startReplacement + replaceCount); - - // Now update the number of elements. - this.selection.selectEventsSuppressed = true; - - // First remove the old elements - this._rows.splice(startReplacement, replaceCount); - - // If the container is now closed, we're done. - if (!aContainer.containerOpen) { - let oldSelectionCount = this.selection.count; - if (replaceCount) - this._tree.rowCountChanged(startReplacement, -replaceCount); - - // Select the row next to the closed container if any of its - // children were selected, and nothing else is selected. - if (nodesToReselect.length > 0 && - nodesToReselect.length == oldSelectionCount) { - this.selection.rangedSelect(startReplacement, startReplacement, true); - this._tree.ensureRowIsVisible(startReplacement); - } - - this.selection.selectEventsSuppressed = false; - return; - } - - // Otherwise, start a batch first. - this._tree.beginUpdateBatch(); - if (replaceCount) - this._tree.rowCountChanged(startReplacement, -replaceCount); - - let toOpenElements = []; - let elementsAddedCount = this._buildVisibleSection(aContainer, - startReplacement, - toOpenElements); - if (elementsAddedCount) - this._tree.rowCountChanged(startReplacement, elementsAddedCount); - - if (!this._flatList) { - // Now, open any containers that were persisted. - for (let i = 0; i < toOpenElements.length; i++) { - let item = toOpenElements[i]; - let parent = item.parent; - - // Avoid recursively opening containers. - while (parent) { - if (parent.uri == item.uri) - break; - parent = parent.parent; - } - - // If we don't have a parent, we made it all the way to the root - // and didn't find a match, so we can open our item. - if (!parent && !item.containerOpen) - item.containerOpen = true; - } - } - - if (this._controller.hasCachedLivemarkInfo(aContainer)) { - let queryOptions = PlacesUtils.asQuery(this._result.root).queryOptions; - if (!queryOptions.excludeItems) { - this._populateLivemarkContainer(aContainer); - } - } - - this._tree.endUpdateBatch(); - - // Restore selection. - this._restoreSelection(nodesToReselect, aContainer); - this.selection.selectEventsSuppressed = false; - }, - - _columns: [], - _findColumnByType: function PTV__findColumnByType(aColumnType) { - if (this._columns[aColumnType]) - return this._columns[aColumnType]; - - let columns = this._tree.columns; - let colCount = columns.count; - for (let i = 0; i < colCount; i++) { - let column = columns.getColumnAt(i); - let columnType = this._getColumnType(column); - this._columns[columnType] = column; - if (columnType == aColumnType) - return column; - } - - // That's completely valid. Most of our trees actually include just the - // title column. - return null; - }, - - sortingChanged: function PTV__sortingChanged(aSortingMode) { - if (!this._tree || !this._result) - return; - - // Depending on the sort mode, certain commands may be disabled. - window.updateCommands("sort"); - - let columns = this._tree.columns; - - // Clear old sorting indicator. - let sortedColumn = columns.getSortedColumn(); - if (sortedColumn) - sortedColumn.element.removeAttribute("sortDirection"); - - // Set new sorting indicator by looking through all columns for ours. - if (aSortingMode == Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) - return; - - let [desiredColumn, desiredIsDescending] = - this._sortTypeToColumnType(aSortingMode); - let colCount = columns.count; - let column = this._findColumnByType(desiredColumn); - if (column) { - let sortDir = desiredIsDescending ? "descending" : "ascending"; - column.element.setAttribute("sortDirection", sortDir); - } - }, - - _inBatchMode: false, - batching: function PTV__batching(aToggleMode) { - if (this._inBatchMode != aToggleMode) { - this._inBatchMode = this.selection.selectEventsSuppressed = aToggleMode; - if (this._inBatchMode) { - this._tree.beginUpdateBatch(); - } - else { - this._tree.endUpdateBatch(); - } - } - }, - - get result() this._result, - set result(val) { - if (this._result) { - this._result.removeObserver(this); - this._rootNode.containerOpen = false; - } - - if (val) { - this._result = val; - this._rootNode = this._result.root; - this._cellProperties = new Map(); - this._cuttingNodes = new Set(); - } - else if (this._result) { - delete this._result; - delete this._rootNode; - delete this._cellProperties; - delete this._cuttingNodes; - } - - // If the tree is not set yet, setTree will call finishInit. - if (this._tree && val) - this._finishInit(); - - return val; - }, - - nodeForTreeIndex: function PTV_nodeForTreeIndex(aIndex) { - if (aIndex > this._rows.length) - throw Cr.NS_ERROR_INVALID_ARG; - - return this._getNodeForRow(aIndex); - }, - - treeIndexForNode: function PTV_treeNodeForIndex(aNode) { - // The API allows passing invisible nodes. - try { - return this._getRowForNode(aNode, true); - } - catch(ex) { } - - return Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE; - }, - - _getResourceForNode: function PTV_getResourceForNode(aNode) - { - let uri = aNode.uri; - NS_ASSERT(uri, "if there is no uri, we can't persist the open state"); - return uri ? PlacesUIUtils.RDF.GetResource(uri) : null; - }, - - // nsITreeView - get rowCount() this._rows.length, - get selection() this._selection, - set selection(val) this._selection = val, - - getRowProperties: function() { return ""; }, - - getCellProperties: - function PTV_getCellProperties(aRow, aColumn) { - // for anonid-trees, we need to add the column-type manually - var props = ""; - let columnType = aColumn.element.getAttribute("anonid"); - if (columnType) - props += columnType; - else - columnType = aColumn.id; - - // Set the "ltr" property on url cells - if (columnType == "url") - props += " ltr"; - - if (columnType != "title") - return props; - - let node = this._getNodeForRow(aRow); - - if (this._cuttingNodes.has(node)) { - props += " cutting"; - } - - let properties = this._cellProperties.get(node); - if (properties === undefined) { - properties = ""; - let itemId = node.itemId; - let nodeType = node.type; - if (PlacesUtils.containerTypes.indexOf(nodeType) != -1) { - if (nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY) { - properties += " query"; - if (PlacesUtils.nodeIsTagQuery(node)) - properties += " tagContainer"; - else if (PlacesUtils.nodeIsDay(node)) - properties += " dayContainer"; - else if (PlacesUtils.nodeIsHost(node)) - properties += " hostContainer"; - } - else if (nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER || - nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT) { - if (this._controller.hasCachedLivemarkInfo(node)) { - properties += " livemark"; - } - else { - PlacesUtils.livemarks.getLivemark({ id: node.itemId }) - .then(aLivemark => { - this._controller.cacheLivemarkInfo(node, aLivemark); - let props = this._cellProperties.get(node); - this._cellProperties.set(node, props += " livemark"); - // The livemark attribute is set as a cell property on the title cell. - this._invalidateCellValue(node, this.COLUMN_TYPE_TITLE); - }, () => undefined); - } - } - - if (itemId != -1) { - let queryName = PlacesUIUtils.getLeftPaneQueryNameFromId(itemId); - if (queryName) - properties += " OrganizerQuery_" + queryName; - } - } - else if (nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) - properties += " separator"; - else if (PlacesUtils.nodeIsURI(node)) { - properties += " " + PlacesUIUtils.guessUrlSchemeForUI(node.uri); - - if (this._controller.hasCachedLivemarkInfo(node.parent)) { - properties += " livemarkItem"; - if (node.accessCount) { - properties += " visited"; - } - } - } - - this._cellProperties.set(node, properties); - } - - return props + " " + properties; - }, - - getColumnProperties: function(aColumn) { return ""; }, - - isContainer: function PTV_isContainer(aRow) { - // Only leaf nodes aren't listed in the rows array. - let node = this._rows[aRow]; - if (node === undefined) - return false; - - if (PlacesUtils.nodeIsContainer(node)) { - // Flat-lists may ignore expandQueries and other query options when - // they are asked to open a container. - if (this._flatList) - return true; - - // treat non-expandable childless queries as non-containers - if (PlacesUtils.nodeIsQuery(node)) { - let parent = node.parent; - if ((PlacesUtils.nodeIsQuery(parent) || - PlacesUtils.nodeIsFolder(parent)) && - !PlacesUtils.asQuery(node).hasChildren) - return PlacesUtils.asQuery(parent).queryOptions.expandQueries; - } - return true; - } - return false; - }, - - isContainerOpen: function PTV_isContainerOpen(aRow) { - if (this._flatList) - return false; - - // All containers are listed in the rows array. - return this._rows[aRow].containerOpen; - }, - - isContainerEmpty: function PTV_isContainerEmpty(aRow) { - if (this._flatList) - return true; - - let node = this._rows[aRow]; - if (this._controller.hasCachedLivemarkInfo(node)) { - let queryOptions = PlacesUtils.asQuery(this._result.root).queryOptions; - return queryOptions.excludeItems; - } - - // All containers are listed in the rows array. - return !node.hasChildren; - }, - - isSeparator: function PTV_isSeparator(aRow) { - // All separators are listed in the rows array. - let node = this._rows[aRow]; - return node && PlacesUtils.nodeIsSeparator(node); - }, - - isSorted: function PTV_isSorted() { - return this._result.sortingMode != - Ci.nsINavHistoryQueryOptions.SORT_BY_NONE; - }, - - canDrop: function PTV_canDrop(aRow, aOrientation, aDataTransfer) { - if (!this._result) - throw Cr.NS_ERROR_UNEXPECTED; - - // Drop position into a sorted treeview would be wrong. - if (this.isSorted()) - return false; - - let ip = this._getInsertionPoint(aRow, aOrientation); - return ip && PlacesControllerDragHelper.canDrop(ip, aDataTransfer); - }, - - _getInsertionPoint: function PTV__getInsertionPoint(index, orientation) { - let container = this._result.root; - let dropNearItemId = -1; - // When there's no selection, assume the container is the container - // the view is populated from (i.e. the result's itemId). - if (index != -1) { - let lastSelected = this.nodeForTreeIndex(index); - if (this.isContainer(index) && orientation == Ci.nsITreeView.DROP_ON) { - // If the last selected item is an open container, append _into_ - // it, rather than insert adjacent to it. - container = lastSelected; - index = -1; - } - else if (lastSelected.containerOpen && - orientation == Ci.nsITreeView.DROP_AFTER && - lastSelected.hasChildren) { - // If the last selected node is an open container and the user is - // trying to drag into it as a first node, really insert into it. - container = lastSelected; - orientation = Ci.nsITreeView.DROP_ON; - index = 0; - } - else { - // Use the last-selected node's container. - container = lastSelected.parent; - - // During its Drag & Drop operation, the tree code closes-and-opens - // containers very often (part of the XUL "spring-loaded folders" - // implementation). And in certain cases, we may reach a closed - // container here. However, we can simply bail out when this happens, - // because we would then be back here in less than a millisecond, when - // the container had been reopened. - if (!container || !container.containerOpen) - return null; - - // Avoid the potentially expensive call to getChildIndex - // if we know this container doesn't allow insertion. - if (PlacesControllerDragHelper.disallowInsertion(container)) - return null; - - let queryOptions = PlacesUtils.asQuery(this._result.root).queryOptions; - if (queryOptions.sortingMode != - Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) { - // If we are within a sorted view, insert at the end. - index = -1; - } - else if (queryOptions.excludeItems || - queryOptions.excludeQueries || - queryOptions.excludeReadOnlyFolders) { - // Some item may be invisible, insert near last selected one. - // We don't replace index here to avoid requests to the db, - // instead it will be calculated later by the controller. - index = -1; - dropNearItemId = lastSelected.itemId; - } - else { - let lsi = container.getChildIndex(lastSelected); - index = orientation == Ci.nsITreeView.DROP_BEFORE ? lsi : lsi + 1; - } - } - } - - if (PlacesControllerDragHelper.disallowInsertion(container)) - return null; - - return new InsertionPoint(PlacesUtils.getConcreteItemId(container), - index, orientation, - PlacesUtils.nodeIsTagQuery(container), - dropNearItemId); - }, - - drop: function PTV_drop(aRow, aOrientation, aDataTransfer) { - // We are responsible for translating the |index| and |orientation| - // parameters into a container id and index within the container, - // since this information is specific to the tree view. - let ip = this._getInsertionPoint(aRow, aOrientation); - if (ip) - PlacesControllerDragHelper.onDrop(ip, aDataTransfer); - - PlacesControllerDragHelper.currentDropTarget = null; - }, - - getParentIndex: function PTV_getParentIndex(aRow) { - let [parentNode, parentRow] = this._getParentByChildRow(aRow); - return parentRow; - }, - - hasNextSibling: function PTV_hasNextSibling(aRow, aAfterIndex) { - if (aRow == this._rows.length - 1) { - // The last row has no sibling. - return false; - } - - let node = this._rows[aRow]; - if (node === undefined || this._isPlainContainer(node.parent)) { - // The node is a child of a plain container. - // If the next row is either unset or has the same parent, - // it's a sibling. - let nextNode = this._rows[aRow + 1]; - return (nextNode == undefined || nextNode.parent == node.parent); - } - - let thisLevel = node.indentLevel; - for (let i = aAfterIndex + 1; i < this._rows.length; ++i) { - let rowNode = this._getNodeForRow(i); - let nextLevel = rowNode.indentLevel; - if (nextLevel == thisLevel) - return true; - if (nextLevel < thisLevel) - break; - } - - return false; - }, - - getLevel: function(aRow) this._getNodeForRow(aRow).indentLevel, - - getImageSrc: function PTV_getImageSrc(aRow, aColumn) { - // Only the title column has an image. - if (this._getColumnType(aColumn) != this.COLUMN_TYPE_TITLE) - return ""; - - return this._getNodeForRow(aRow).icon; - }, - - getProgressMode: function(aRow, aColumn) { }, - getCellValue: function(aRow, aColumn) { }, - - getCellText: function PTV_getCellText(aRow, aColumn) { - let node = this._getNodeForRow(aRow); - switch (this._getColumnType(aColumn)) { - case this.COLUMN_TYPE_TITLE: - // normally, this is just the title, but we don't want empty items in - // the tree view so return a special string if the title is empty. - // Do it here so that callers can still get at the 0 length title - // if they go through the "result" API. - if (PlacesUtils.nodeIsSeparator(node)) - return ""; - return PlacesUIUtils.getBestTitle(node, true); - case this.COLUMN_TYPE_TAGS: - return node.tags; - case this.COLUMN_TYPE_URI: - if (PlacesUtils.nodeIsURI(node)) - return node.uri; - return ""; - case this.COLUMN_TYPE_DATE: - let nodeTime = node.time; - if (nodeTime == 0 || !PlacesUtils.nodeIsURI(node)) { - // hosts and days shouldn't have a value for the date column. - // Actually, you could argue this point, but looking at the - // results, seeing the most recently visited date is not what - // I expect, and gives me no information I know how to use. - // Only show this for URI-based items. - return ""; - } - - return this._convertPRTimeToString(nodeTime); - case this.COLUMN_TYPE_VISITCOUNT: - return node.accessCount; - case this.COLUMN_TYPE_KEYWORD: - if (PlacesUtils.nodeIsBookmark(node)) - return PlacesUtils.bookmarks.getKeywordForBookmark(node.itemId); - return ""; - case this.COLUMN_TYPE_DESCRIPTION: - if (node.itemId != -1) { - try { - return PlacesUtils.annotations. - getItemAnnotation(node.itemId, PlacesUIUtils.DESCRIPTION_ANNO); - } - catch (ex) { /* has no description */ } - } - return ""; - case this.COLUMN_TYPE_DATEADDED: - if (node.dateAdded) - return this._convertPRTimeToString(node.dateAdded); - return ""; - case this.COLUMN_TYPE_LASTMODIFIED: - if (node.lastModified) - return this._convertPRTimeToString(node.lastModified); - return ""; - case this.COLUMN_TYPE_PARENTFOLDER: - if (PlacesUtils.nodeIsQuery(node.parent) && - PlacesUtils.asQuery(node.parent).queryOptions.queryType == - Components.interfaces.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY && node.uri) - return ""; - var bmsvc = Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"]. - getService(Components.interfaces.nsINavBookmarksService); - var rowId = node.itemId; - try { - var parentFolderId = bmsvc.getFolderIdForItem(rowId); - var folderTitle = bmsvc.getItemTitle(parentFolderId); - } catch(ex) { - var folderTitle = ""; - } - return folderTitle; - case this.COLUMN_TYPE_PARENTFOLDERPATH: - if (PlacesUtils.nodeIsQuery(node.parent) && - PlacesUtils.asQuery(node.parent).queryOptions.queryType == - Components.interfaces.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY && node.uri) - return ""; - var bmsvc = Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"]. - getService(Components.interfaces.nsINavBookmarksService); - var rowId = node.itemId; - try { - var FolderId; - var parentFolderId = bmsvc.getFolderIdForItem(rowId); - var folderTitle = bmsvc.getItemTitle(parentFolderId); - while ((FolderId = bmsvc.getFolderIdForItem(parentFolderId))) { - if (FolderId == parentFolderId) - break; - parentFolderId = FolderId; - var text = bmsvc.getItemTitle(parentFolderId); - if (!text) - break; - folderTitle = text + " /"+ folderTitle; - } - folderTitle = folderTitle.replace(/^\s/,""); - } catch(ex) { - var folderTitle = ""; - } - return folderTitle; - } - return ""; - }, - - setTree: function PTV_setTree(aTree) { - // If we are replacing the tree during a batch, there is a concrete risk - // that the treeView goes out of sync, thus it's safer to end the batch now. - // This is a no-op if we are not batching. - this.batching(false); - - let hasOldTree = this._tree != null; - this._tree = aTree; - - if (this._result) { - if (hasOldTree) { - // detach from result when we are detaching from the tree. - // This breaks the reference cycle between us and the result. - if (!aTree) { - this._result.removeObserver(this); - this._rootNode.containerOpen = false; - } - } - if (aTree) - this._finishInit(); - } - }, - - toggleOpenState: function PTV_toggleOpenState(aRow) { - if (!this._result) - throw Cr.NS_ERROR_UNEXPECTED; - - let node = this._rows[aRow]; - if (this._flatList && this._openContainerCallback) { - this._openContainerCallback(node); - return; - } - - // Persist containers open status, but never persist livemarks. - if (!this._controller.hasCachedLivemarkInfo(node)) { - let resource = this._getResourceForNode(node); - if (resource) { - const openLiteral = PlacesUIUtils.RDF.GetResource("http://home.netscape.com/NC-rdf#open"); - const trueLiteral = PlacesUIUtils.RDF.GetLiteral("true"); - - if (node.containerOpen) - PlacesUIUtils.localStore.Unassert(resource, openLiteral, trueLiteral); - else - PlacesUIUtils.localStore.Assert(resource, openLiteral, trueLiteral, true); - } - } - - node.containerOpen = !node.containerOpen; - }, - - cycleHeader: function PTV_cycleHeader(aColumn) { - if (!this._result) - throw Cr.NS_ERROR_UNEXPECTED; - - // Sometimes you want a tri-state sorting, and sometimes you don't. This - // rule allows tri-state sorting when the root node is a folder. This will - // catch the most common cases. When you are looking at folders, you want - // the third state to reset the sorting to the natural bookmark order. When - // you are looking at history, that third state has no meaning so we try - // to disallow it. - // - // The problem occurs when you have a query that results in bookmark - // folders. One example of this is the subscriptions view. In these cases, - // this rule doesn't allow you to sort those sub-folders by their natural - // order. - let allowTriState = PlacesUtils.nodeIsFolder(this._result.root); - - let oldSort = this._result.sortingMode; - let oldSortingAnnotation = this._result.sortingAnnotation; - let newSort; - let newSortingAnnotation = ""; - const NHQO = Ci.nsINavHistoryQueryOptions; - switch (this._getColumnType(aColumn)) { - case this.COLUMN_TYPE_TITLE: - if (oldSort == NHQO.SORT_BY_TITLE_ASCENDING) - newSort = NHQO.SORT_BY_TITLE_DESCENDING; - else if (allowTriState && oldSort == NHQO.SORT_BY_TITLE_DESCENDING) - newSort = NHQO.SORT_BY_NONE; - else - newSort = NHQO.SORT_BY_TITLE_ASCENDING; - - break; - case this.COLUMN_TYPE_URI: - if (oldSort == NHQO.SORT_BY_URI_ASCENDING) - newSort = NHQO.SORT_BY_URI_DESCENDING; - else if (allowTriState && oldSort == NHQO.SORT_BY_URI_DESCENDING) - newSort = NHQO.SORT_BY_NONE; - else - newSort = NHQO.SORT_BY_URI_ASCENDING; - - break; - case this.COLUMN_TYPE_DATE: - if (oldSort == NHQO.SORT_BY_DATE_ASCENDING) - newSort = NHQO.SORT_BY_DATE_DESCENDING; - else if (allowTriState && - oldSort == NHQO.SORT_BY_DATE_DESCENDING) - newSort = NHQO.SORT_BY_NONE; - else - newSort = NHQO.SORT_BY_DATE_ASCENDING; - - break; - case this.COLUMN_TYPE_VISITCOUNT: - // visit count default is unusual because we sort by descending - // by default because you are most likely to be looking for - // highly visited sites when you click it - if (oldSort == NHQO.SORT_BY_VISITCOUNT_DESCENDING) - newSort = NHQO.SORT_BY_VISITCOUNT_ASCENDING; - else if (allowTriState && oldSort == NHQO.SORT_BY_VISITCOUNT_ASCENDING) - newSort = NHQO.SORT_BY_NONE; - else - newSort = NHQO.SORT_BY_VISITCOUNT_DESCENDING; - - break; - case this.COLUMN_TYPE_KEYWORD: - if (oldSort == NHQO.SORT_BY_KEYWORD_ASCENDING) - newSort = NHQO.SORT_BY_KEYWORD_DESCENDING; - else if (allowTriState && oldSort == NHQO.SORT_BY_KEYWORD_DESCENDING) - newSort = NHQO.SORT_BY_NONE; - else - newSort = NHQO.SORT_BY_KEYWORD_ASCENDING; - - break; - case this.COLUMN_TYPE_DESCRIPTION: - if (oldSort == NHQO.SORT_BY_ANNOTATION_ASCENDING && - oldSortingAnnotation == PlacesUIUtils.DESCRIPTION_ANNO) { - newSort = NHQO.SORT_BY_ANNOTATION_DESCENDING; - newSortingAnnotation = PlacesUIUtils.DESCRIPTION_ANNO; - } - else if (allowTriState && - oldSort == NHQO.SORT_BY_ANNOTATION_DESCENDING && - oldSortingAnnotation == PlacesUIUtils.DESCRIPTION_ANNO) - newSort = NHQO.SORT_BY_NONE; - else { - newSort = NHQO.SORT_BY_ANNOTATION_ASCENDING; - newSortingAnnotation = PlacesUIUtils.DESCRIPTION_ANNO; - } - - break; - case this.COLUMN_TYPE_DATEADDED: - if (oldSort == NHQO.SORT_BY_DATEADDED_ASCENDING) - newSort = NHQO.SORT_BY_DATEADDED_DESCENDING; - else if (allowTriState && - oldSort == NHQO.SORT_BY_DATEADDED_DESCENDING) - newSort = NHQO.SORT_BY_NONE; - else - newSort = NHQO.SORT_BY_DATEADDED_ASCENDING; - - break; - case this.COLUMN_TYPE_LASTMODIFIED: - if (oldSort == NHQO.SORT_BY_LASTMODIFIED_ASCENDING) - newSort = NHQO.SORT_BY_LASTMODIFIED_DESCENDING; - else if (allowTriState && - oldSort == NHQO.SORT_BY_LASTMODIFIED_DESCENDING) - newSort = NHQO.SORT_BY_NONE; - else - newSort = NHQO.SORT_BY_LASTMODIFIED_ASCENDING; - - break; - case this.COLUMN_TYPE_TAGS: - if (oldSort == NHQO.SORT_BY_TAGS_ASCENDING) - newSort = NHQO.SORT_BY_TAGS_DESCENDING; - else if (allowTriState && oldSort == NHQO.SORT_BY_TAGS_DESCENDING) - newSort = NHQO.SORT_BY_NONE; - else - newSort = NHQO.SORT_BY_TAGS_ASCENDING; - - break; - case this.COLUMN_TYPE_PARENTFOLDER: - return; - - break; - case this.COLUMN_TYPE_PARENTFOLDERPATH: - return; - - break; - default: - throw Cr.NS_ERROR_INVALID_ARG; - } - this._result.sortingAnnotation = newSortingAnnotation; - this._result.sortingMode = newSort; - }, - - isEditable: function PTV_isEditable(aRow, aColumn) { - // At this point we only support editing the title field. - if (aColumn.index != 0) - return false; - - let node = this._rows[aRow]; - if (!node) { - Cu.reportError("isEditable called for an unbuilt row."); - return false; - } - let itemId = node.itemId; - - // Only bookmark-nodes are editable. Fortunately, this check also takes - // care of livemark children. - if (itemId == -1) - return false; - - // The following items are also not editable, even though they are bookmark - // items. - // * places-roots - // * the left pane special folders and queries (those are place: uri - // bookmarks) - // * separators - // - // Note that concrete itemIds aren't used intentionally. For example, we - // have no reason to disallow renaming a shortcut to the Bookmarks Toolbar, - // except for the one under All Bookmarks. - if (PlacesUtils.nodeIsSeparator(node) || PlacesUtils.isRootItem(itemId)) - return false; - - let parentId = PlacesUtils.getConcreteItemId(node.parent); - if (parentId == PlacesUIUtils.leftPaneFolderId || - parentId == PlacesUIUtils.allBookmarksFolderId) { - // Note that the for the time being this is the check that actually - // blocks renaming places "roots", and not the isRootItem check above. - // That's because places root are only exposed through folder shortcuts - // descendants of the left pane folder. - return false; - } - - return true; - }, - - setCellText: function PTV_setCellText(aRow, aColumn, aText) { - // We may only get here if the cell is editable. - let node = this._rows[aRow]; - if (node.title != aText) { - let txn = new PlacesEditItemTitleTransaction(node.itemId, aText); - PlacesUtils.transactionManager.doTransaction(txn); - } - }, - - toggleCutNode: function PTV_toggleCutNode(aNode, aValue) { - let currentVal = this._cuttingNodes.has(aNode); - if (currentVal != aValue) { - if (aValue) - this._cuttingNodes.add(aNode); - else - this._cuttingNodes.delete(aNode); - - this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE); - } - }, - - selectionChanged: function() { }, - cycleCell: function(aRow, aColumn) { }, - isSelectable: function(aRow, aColumn) { return false; }, - performAction: function(aAction) { }, - performActionOnRow: function(aAction, aRow) { }, - performActionOnCell: function(aAction, aRow, aColumn) { } -}; diff --git a/components/places/jar.mn b/components/places/jar.mn deleted file mode 100644 index 41222e1..0000000 --- a/components/places/jar.mn +++ /dev/null @@ -1,34 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -browser.jar: -% overlay chrome://browser/content/places/places.xul chrome://browser/content/places/downloadsViewOverlay.xul -# Provide another URI for the bookmarkProperties dialog so we can persist the -# attributes separately - content/browser/places/bookmarkProperties2.xul (content/bookmarkProperties.xul) -* content/browser/places/places.xul (content/places.xul) -* content/browser/places/places.js (content/places.js) - content/browser/places/places.css (content/places.css) - content/browser/places/organizer.css (content/organizer.css) - content/browser/places/bookmarkProperties.xul (content/bookmarkProperties.xul) - content/browser/places/bookmarkProperties.js (content/bookmarkProperties.js) - content/browser/places/placesOverlay.xul (content/placesOverlay.xul) -* content/browser/places/menu.xml (content/menu.xml) - content/browser/places/tree.xml (content/tree.xml) - content/browser/places/controller.js (content/controller.js) - content/browser/places/treeView.js (content/treeView.js) -* content/browser/places/browserPlacesViews.js (content/browserPlacesViews.js) -# keep the Places version of the history sidebar at history/history-panel.xul -# to prevent having to worry about between versions of the browser -* content/browser/history/history-panel.xul (content/history-panel.xul) - content/browser/places/history-panel.js (content/history-panel.js) -# ditto for the bookmarks sidebar - content/browser/bookmarks/bookmarksPanel.xul (content/bookmarksPanel.xul) - content/browser/bookmarks/bookmarksPanel.js (content/bookmarksPanel.js) -* content/browser/bookmarks/sidebarUtils.js (content/sidebarUtils.js) - content/browser/places/moveBookmarks.xul (content/moveBookmarks.xul) - content/browser/places/moveBookmarks.js (content/moveBookmarks.js) - content/browser/places/editBookmarkOverlay.xul (content/editBookmarkOverlay.xul) - content/browser/places/editBookmarkOverlay.js (content/editBookmarkOverlay.js) -* content/browser/places/downloadsViewOverlay.xul (content/downloadsViewOverlay.xul) diff --git a/components/places/moz.build b/components/places/moz.build deleted file mode 100644 index f8b0d12..0000000 --- a/components/places/moz.build +++ /dev/null @@ -1,9 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -JAR_MANIFESTS += ['jar.mn'] - -EXTRA_JS_MODULES += [ 'PlacesUIUtils.jsm' ] diff --git a/components/preferences/advanced.js b/components/preferences/advanced.js deleted file mode 100644 index aab58b3..0000000 --- a/components/preferences/advanced.js +++ /dev/null @@ -1,755 +0,0 @@ -# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -// Load DownloadUtils module for convertByteUnits -Components.utils.import("resource://gre/modules/DownloadUtils.jsm"); -Components.utils.import("resource://gre/modules/ctypes.jsm"); -Components.utils.import("resource://gre/modules/Services.jsm"); -Components.utils.import("resource://gre/modules/LoadContextInfo.jsm"); -Components.utils.import("resource://gre/modules/BrowserUtils.jsm"); - -var gAdvancedPane = { - _inited: false, - - /** - * Brings the appropriate tab to the front and initializes various bits of UI. - */ - init: function () - { - this._inited = true; - var advancedPrefs = document.getElementById("advancedPrefs"); - - var extraArgs = window.arguments[1]; - if (extraArgs && extraArgs["advancedTab"]){ - advancedPrefs.selectedTab = document.getElementById(extraArgs["advancedTab"]); - } else { - var preference = document.getElementById("browser.preferences.advanced.selectedTabIndex"); - if (preference.value !== null) - advancedPrefs.selectedIndex = preference.value; - } - -#ifdef HAVE_SHELL_SERVICE - this.updateSetDefaultBrowser(); -#ifdef XP_WIN - // In Windows 8 we launch the control panel since it's the only - // way to get all file type association prefs. So we don't know - // when the user will select the default. We refresh here periodically - // in case the default changes. On other Windows OS's defaults can also - // be set while the prefs are open. - window.setInterval(this.updateSetDefaultBrowser, 1000); -#endif -#endif - -#ifdef MOZ_UPDATER - this.updateReadPrefs(); -#endif - this.updateOfflineAppsPermissions(); - this.updateOfflineApps(); - - this.updateActualCacheSize(); - this.updateActualAppCacheSize(); - - // Notify observers that the UI is now ready - Services.obs.notifyObservers(window, "advanced-pane-loaded", null); - }, - - /** - * Stores the identity of the current tab in preferences so that the selected - * tab can be persisted between openings of the preferences window. - */ - tabSelectionChanged: function () - { - if (!this._inited) - return; - var advancedPrefs = document.getElementById("advancedPrefs"); - var preference = document.getElementById("browser.preferences.advanced.selectedTabIndex"); - preference.valueFromPreferences = advancedPrefs.selectedIndex; - }, - - // GENERAL TAB - - /* - * Preferences: - * - * accessibility.browsewithcaret - * - true enables keyboard navigation and selection within web pages using a - * visible caret, false uses normal keyboard navigation with no caret - * accessibility.typeaheadfind - * - when set to true, typing outside text areas and input boxes will - * automatically start searching for what's typed within the current - * document; when set to false, no search action happens - * general.autoScroll - * - when set to true, clicking the scroll wheel on the mouse activates a - * mouse mode where moving the mouse down scrolls the document downward with - * speed correlated with the distance of the cursor from the original - * position at which the click occurred (and likewise with movement upward); - * if false, this behavior is disabled - * general.smoothScroll - * - set to true to enable finer page scrolling than line-by-line on page-up, - * page-down, and other such page movements - * layout.spellcheckDefault - * - an integer: - * 0 disables spellchecking - * 1 enables spellchecking, but only for multiline text fields - * 2 enables spellchecking for all text fields - */ - - /** - * Stores the original value of the spellchecking preference to enable proper - * restoration if unchanged (since we're mapping a tristate onto a checkbox). - */ - _storedSpellCheck: 0, - - /** - * Returns true if any spellchecking is enabled and false otherwise, caching - * the current value to enable proper pref restoration if the checkbox is - * never changed. - */ - readCheckSpelling: function () - { - var pref = document.getElementById("layout.spellcheckDefault"); - this._storedSpellCheck = pref.value; - - return (pref.value != 0); - }, - - /** - * Returns the value of the spellchecking preference represented by UI, - * preserving the preference's "hidden" value if the preference is - * unchanged and represents a value not strictly allowed in UI. - */ - writeCheckSpelling: function () - { - var checkbox = document.getElementById("checkSpelling"); - return checkbox.checked ? (this._storedSpellCheck == 2 ? 2 : 1) : 0; - }, - - /** - * security.OCSP.enabled is an integer value for legacy reasons. - * A value of 1 means OCSP is enabled. Any other value means it is disabled. - */ - readEnableOCSP: function () - { - var preference = document.getElementById("security.OCSP.enabled"); - // This is the case if the preference is the default value. - if (preference.value === undefined) { - return true; - } - return preference.value == 1; - }, - - /** - * See documentation for readEnableOCSP. - */ - writeEnableOCSP: function () - { - var checkbox = document.getElementById("enableOCSP"); - return checkbox.checked ? 1 : 0; - }, - - /** - * When the user toggles the layers.acceleration.disabled pref, - * sync its new value to the gfx.direct2d.disabled pref too. - */ - updateHardwareAcceleration: function() - { -#ifdef XP_WIN - var fromPref = document.getElementById("layers.acceleration.disabled"); - var toPref = document.getElementById("gfx.direct2d.disabled"); - toPref.value = fromPref.value; -#endif - }, - - // DATA CHOICES TAB - - /** - * opening links behind a modal dialog is poor form. Work around flawed text-link handling here. - */ - openTextLink: function (evt) { - let where = Services.prefs.getBoolPref("browser.preferences.instantApply") ? "tab" : "window"; - openUILinkIn(evt.target.getAttribute("href"), where); - evt.preventDefault(); - }, - - /** - * Set up or hide the Learn More links for various data collection options - */ - _setupLearnMoreLink: function (pref, element) { - // set up the Learn More link with the correct URL - let url = Services.prefs.getCharPref(pref); - let el = document.getElementById(element); - - if (url) { - el.setAttribute("href", url); - } else { - el.setAttribute("hidden", "true"); - } - }, - - // NETWORK TAB - - /* - * Preferences: - * - * browser.cache.disk.capacity - * - the size of the browser cache in KB - * - Only used if browser.cache.disk.smart_size.enabled is disabled - */ - - /** - * Displays a dialog in which proxy settings may be changed. - */ - showConnections: function () - { - document.documentElement.openSubDialog("chrome://browser/content/preferences/connection.xul", - "", null); - }, - - // Retrieves the amount of space currently used by disk cache - updateActualCacheSize: function () - { - var sum = 0; - function updateUI(consumption) { - var actualSizeLabel = document.getElementById("actualDiskCacheSize"); - var sizeStrings = DownloadUtils.convertByteUnits(consumption); - var prefStrBundle = document.getElementById("bundlePreferences"); - var sizeStr = prefStrBundle.getFormattedString("actualDiskCacheSize", sizeStrings); - actualSizeLabel.value = sizeStr; - } - - Visitor.prototype = { - expected: 0, - sum: 0, - QueryInterface: function listener_qi(iid) { - if (iid.equals(Components.interfaces.nsISupports) || - iid.equals(Components.interfaces.nsICacheStorageVisitor)) { - return this; - } - throw Components.results.NS_ERROR_NO_INTERFACE; - }, - onCacheStorageInfo: function(num, consumption) - { - this.sum += consumption; - if (!--this.expected) - updateUI(this.sum); - } - }; - function Visitor(callbacksExpected) { - this.expected = callbacksExpected; - } - - var cacheService = - Components.classes["@mozilla.org/netwerk/cache-storage-service;1"] - .getService(Components.interfaces.nsICacheStorageService); - // non-anonymous - var storage1 = cacheService.diskCacheStorage(LoadContextInfo.default, false); - // anonymous - var storage2 = cacheService.diskCacheStorage(LoadContextInfo.anonymous, false); - - // expect 2 callbacks - var visitor = new Visitor(2); - storage1.asyncVisitStorage(visitor, false /* Do not walk entries */); - storage2.asyncVisitStorage(visitor, false /* Do not walk entries */); - }, - - // Retrieves the amount of space currently used by offline cache - updateActualAppCacheSize: function () - { - var visitor = { - onCacheStorageInfo: function (aEntryCount, aConsumption, aCapacity, aDiskDirectory) - { - var actualSizeLabel = document.getElementById("actualAppCacheSize"); - var sizeStrings = DownloadUtils.convertByteUnits(aConsumption); - var prefStrBundle = document.getElementById("bundlePreferences"); - var sizeStr = prefStrBundle.getFormattedString("actualAppCacheSize", sizeStrings); - actualSizeLabel.value = sizeStr; - } - }; - - var cacheService = - Components.classes["@mozilla.org/netwerk/cache-storage-service;1"] - .getService(Components.interfaces.nsICacheStorageService); - var storage = cacheService.appCacheStorage(LoadContextInfo.default, null); - storage.asyncVisitStorage(visitor, false); - }, - - updateCacheSizeUI: function (smartSizeEnabled) - { - document.getElementById("useCacheBefore").disabled = smartSizeEnabled; - document.getElementById("cacheSize").disabled = smartSizeEnabled; - document.getElementById("useCacheAfter").disabled = smartSizeEnabled; - }, - - readSmartSizeEnabled: function () - { - // The smart_size.enabled preference element is inverted="true", so its - // value is the opposite of the actual pref value - var disabled = document.getElementById("browser.cache.disk.smart_size.enabled").value; - this.updateCacheSizeUI(!disabled); - }, - - /** - * Converts the cache size from units of KB to units of MB and returns that - * value. - */ - readCacheSize: function () - { - var preference = document.getElementById("browser.cache.disk.capacity"); - return preference.value / 1024; - }, - - /** - * Converts the cache size as specified in UI (in MB) to KB and returns that - * value. - */ - writeCacheSize: function () - { - var cacheSize = document.getElementById("cacheSize"); - var intValue = parseInt(cacheSize.value, 10); - return isNaN(intValue) ? 0 : intValue * 1024; - }, - - /** - * Clears the cache. - */ - clearCache: function () - { - var cache = Components.classes["@mozilla.org/netwerk/cache-storage-service;1"] - .getService(Components.interfaces.nsICacheStorageService); - try { - cache.clear(); - } catch(ex) {} - this.updateActualCacheSize(); - }, - - /** - * Clears the application cache. - */ - clearOfflineAppCache: function () - { - Components.utils.import("resource:///modules/offlineAppCache.jsm"); - OfflineAppCacheHelper.clear(); - - this.updateActualAppCacheSize(); - this.updateOfflineApps(); - }, - - updateOfflineAppsPermissions: function() - { - var permPref = document.getElementById("offline-apps.permissions"); - var allowPref = document.getElementById("offline-apps.allow_by_default"); - var notifyPref = document.getElementById("browser.offline-apps.notify"); - switch (permPref.value) { - case 0: allowPref.value = false; - notifyPref.value = false; - break; - case 1: allowPref.value = false; - notifyPref.value = true; - break; - case 2: allowPref.value = true; - notifyPref.value = true; - break; - default: console.error("Preference error: Invalid value ",permPref.value," for offline app permissions - resetting to default."); - permPref.value = 2; - allowPref.value = true; - notifyPref.value = true; - } - // Set state of "Exceptions" button accordingly. - var button = document.getElementById("offlineNotifyExceptions"); - button.disabled = !allowPref.value && !notifyPref.value; - }, - - showOfflineExceptions: function() - { - var bundlePreferences = document.getElementById("bundlePreferences"); - var params = { blockVisible : false, - sessionVisible : false, - allowVisible : false, - prefilledHost : "", - permissionType : "offline-app", - manageCapability : Components.interfaces.nsIPermissionManager.DENY_ACTION, - windowTitle : bundlePreferences.getString("offlinepermissionstitle"), - introText : bundlePreferences.getString("offlinepermissionstext") }; - document.documentElement.openWindow("Browser:Permissions", - "chrome://browser/content/preferences/permissions.xul", - "", params); - }, - - // XXX: duplicated in browser.js - _getOfflineAppUsage: function (perm, groups) - { - var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"]. - getService(Components.interfaces.nsIApplicationCacheService); - if (!groups) - groups = cacheService.getGroups(); - - var ios = Components.classes["@mozilla.org/network/io-service;1"]. - getService(Components.interfaces.nsIIOService); - - var usage = 0; - for (var i = 0; i < groups.length; i++) { - var uri = ios.newURI(groups[i], null, null); - if (perm.matchesURI(uri, true)) { - var cache = cacheService.getActiveCache(groups[i]); - usage += cache.usage; - } - } - - return usage; - }, - - /** - * Updates the list of offline applications - */ - updateOfflineApps: function () - { - var pm = Components.classes["@mozilla.org/permissionmanager;1"] - .getService(Components.interfaces.nsIPermissionManager); - - var list = document.getElementById("offlineAppsList"); - while (list.firstChild) { - list.removeChild(list.firstChild); - } - - var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"]. - getService(Components.interfaces.nsIApplicationCacheService); - var groups = cacheService.getGroups(); - - var bundle = document.getElementById("bundlePreferences"); - - var enumerator = pm.enumerator; - while (enumerator.hasMoreElements()) { - var perm = enumerator.getNext().QueryInterface(Components.interfaces.nsIPermission); - if (perm.type == "offline-app" && - perm.capability != Components.interfaces.nsIPermissionManager.DEFAULT_ACTION && - perm.capability != Components.interfaces.nsIPermissionManager.DENY_ACTION) { - var row = document.createElement("listitem"); - row.id = ""; - row.className = "offlineapp"; - row.setAttribute("origin", perm.principal.origin); - var converted = DownloadUtils. - convertByteUnits(this._getOfflineAppUsage(perm, groups)); - row.setAttribute("usage", - bundle.getFormattedString("offlineAppUsage", - converted)); - list.appendChild(row); - } - } - }, - - offlineAppSelected: function() - { - var removeButton = document.getElementById("offlineAppsListRemove"); - var list = document.getElementById("offlineAppsList"); - if (list.selectedItem) { - removeButton.setAttribute("disabled", "false"); - } else { - removeButton.setAttribute("disabled", "true"); - } - }, - - removeOfflineApp: function() - { - var list = document.getElementById("offlineAppsList"); - var item = list.selectedItem; - var origin = item.getAttribute("origin"); - var principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin); - - var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] - .getService(Components.interfaces.nsIPromptService); - var flags = prompts.BUTTON_TITLE_IS_STRING * prompts.BUTTON_POS_0 + - prompts.BUTTON_TITLE_CANCEL * prompts.BUTTON_POS_1; - - var bundle = document.getElementById("bundlePreferences"); - var title = bundle.getString("offlineAppRemoveTitle"); - var prompt = bundle.getFormattedString("offlineAppRemovePrompt", [principal.URI.prePath]); - var confirm = bundle.getString("offlineAppRemoveConfirm"); - var result = prompts.confirmEx(window, title, prompt, flags, confirm, - null, null, null, {}); - if (result != 0) - return; - - // get the permission - var pm = Components.classes["@mozilla.org/permissionmanager;1"] - .getService(Components.interfaces.nsIPermissionManager); - var perm = pm.getPermissionObject(principal, "offline-app", true); - if (perm) { - // clear offline cache entries - try { - var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"]. - getService(Components.interfaces.nsIApplicationCacheService); - var groups = cacheService.getGroups(); - for (var i = 0; i < groups.length; i++) { - var uri = Services.io.newURI(groups[i], null, null); - if (perm.matchesURI(uri, true)) { - var cache = cacheService.getActiveCache(groups[i]); - cache.discard(); - } - } - } catch (e) {} - - pm.removePermission(perm); - } - list.removeChild(item); - gAdvancedPane.offlineAppSelected(); - this.updateActualAppCacheSize(); - }, - - // UPDATE TAB - - /* - * Preferences: - * - * app.update.enabled - * - true if updates to the application are enabled, false otherwise - * extensions.update.enabled - * - true if updates to extensions and themes are enabled, false otherwise - * browser.search.update - * - true if updates to search engines are enabled, false otherwise - * app.update.auto - * - true if updates should be automatically downloaded and installed, - * possibly with a warning if incompatible extensions are installed (see - * app.update.mode); false if the user should be asked what he wants to do - * when an update is available - * app.update.mode - * - an integer: - * 0 do not warn if an update will disable extensions or themes - * 1 warn if an update will disable extensions or themes - * 2 warn if an update will disable extensions or themes *or* if the - * update is a major update - */ - -#ifdef MOZ_UPDATER - /** - * Selects the item of the radiogroup, and sets the warnIncompatible checkbox - * based on the pref values and locked states. - * - * UI state matrix for update preference conditions - * - * UI Components: Preferences - * Radiogroup i = app.update.enabled - * Warn before disabling extensions checkbox ii = app.update.auto - * iii = app.update.mode - * - * Disabled states: - * Element pref value locked disabled - * radiogroup i t/f f false - * i t/f *t* *true* - * ii t/f f false - * ii t/f *t* *true* - * iii 0/1/2 t/f false - * warnIncompatible i t f false - * i t *t* *true* - * i *f* t/f *true* - * ii t f false - * ii t *t* *true* - * ii *f* t/f *true* - * iii 0/1/2 f false - * iii 0/1/2 *t* *true* - */ - updateReadPrefs: function () - { - var enabledPref = document.getElementById("app.update.enabled"); - var autoPref = document.getElementById("app.update.auto"); - var radiogroup = document.getElementById("updateRadioGroup"); - - if (!enabledPref.value) // Don't care for autoPref.value in this case. - radiogroup.value="manual"; // 3. Never check for updates. - else if (autoPref.value) // enabledPref.value && autoPref.value - radiogroup.value="auto"; // 1. Automatically install updates for Desktop only - else // enabledPref.value && !autoPref.value - radiogroup.value="checkOnly"; // 2. Check, but let me choose - - var canCheck = Components.classes["@mozilla.org/updates/update-service;1"]. - getService(Components.interfaces.nsIApplicationUpdateService). - canCheckForUpdates; - // canCheck is false if the enabledPref is false and locked, - // or the binary platform or OS version is not known. - // A locked pref is sufficient to disable the radiogroup. - radiogroup.disabled = !canCheck || enabledPref.locked || autoPref.locked; - - var modePref = document.getElementById("app.update.mode"); - var warnIncompatible = document.getElementById("warnIncompatible"); - // the warnIncompatible checkbox value is set by readAddonWarn - warnIncompatible.disabled = radiogroup.disabled || modePref.locked || - !enabledPref.value || !autoPref.value; - }, - - /** - * Sets the pref values based on the selected item of the radiogroup, - * and sets the disabled state of the warnIncompatible checkbox accordingly. - */ - updateWritePrefs: function () - { - var enabledPref = document.getElementById("app.update.enabled"); - var autoPref = document.getElementById("app.update.auto"); - var radiogroup = document.getElementById("updateRadioGroup"); - switch (radiogroup.value) { - case "auto": // 1. Automatically install updates for Desktop only - enabledPref.value = true; - autoPref.value = true; - break; - case "checkOnly": // 2. Check, but let me choose - enabledPref.value = true; - autoPref.value = false; - break; - case "manual": // 3. Never check for updates. - enabledPref.value = false; - autoPref.value = false; - } - - var warnIncompatible = document.getElementById("warnIncompatible"); - var modePref = document.getElementById("app.update.mode"); - warnIncompatible.disabled = enabledPref.locked || !enabledPref.value || - autoPref.locked || !autoPref.value || - modePref.locked; - - }, - - /** - * Stores the value of the app.update.mode preference, which is a tristate - * integer preference. We store the value here so that we can properly - * restore the preference value if the UI reflecting the preference value - * is in a state which can represent either of two integer values (as - * opposed to only one possible value in the other UI state). - */ - _modePreference: -1, - - /** - * Reads the app.update.mode preference and converts its value into a - * true/false value for use in determining whether the "Warn me if this will - * disable extensions or themes" checkbox is checked. We also save the value - * of the preference so that the preference value can be properly restored if - * the user's preferences cannot adequately be expressed by a single checkbox. - * - * app.update.mode Checkbox State Meaning - * 0 Unchecked Do not warn - * 1 Checked Warn if there are incompatibilities - * 2 Checked Warn if there are incompatibilities, - * or the update is major. - */ - readAddonWarn: function () - { - var preference = document.getElementById("app.update.mode"); - var warn = preference.value != 0; - gAdvancedPane._modePreference = warn ? preference.value : 1; - return warn; - }, - - /** - * Converts the state of the "Warn me if this will disable extensions or - * themes" checkbox into the integer preference which represents it, - * returning that value. - */ - writeAddonWarn: function () - { - var warnIncompatible = document.getElementById("warnIncompatible"); - return !warnIncompatible.checked ? 0 : gAdvancedPane._modePreference; - }, - - /** - * Displays the history of installed updates. - */ - showUpdates: function () - { - var prompter = Components.classes["@mozilla.org/updates/update-prompt;1"] - .createInstance(Components.interfaces.nsIUpdatePrompt); - prompter.showUpdateHistory(window); - }, -#endif - - // CERTIFICATES TAB - - /* - * Preferences: - * - * security.default_personal_cert - * - a string: - * "Select Automatically" select a certificate automatically when a site - * requests one - * "Ask Every Time" present a dialog to the user so he can select - * the certificate to use on a site which - * requests one - */ - - /** - * Displays the user's certificates and associated options. - */ - showCertificates: function () - { - document.documentElement.openWindow("mozilla:certmanager", - "chrome://pippki/content/certManager.xul", - "", null); - }, - - /** - * Displays a dialog from which the user can manage his security devices. - */ - showSecurityDevices: function () - { - document.documentElement.openWindow("mozilla:devicemanager", - "chrome://pippki/content/device_manager.xul", - "", null); - } -#ifdef HAVE_SHELL_SERVICE - , - - // SYSTEM DEFAULTS - - /* - * Preferences: - * - * browser.shell.checkDefault - * - true if a default-browser check (and prompt to make it so if necessary) - * occurs at startup, false otherwise - */ - - /** - * Show button for setting browser as default browser or information that - * browser is already the default browser. - */ - updateSetDefaultBrowser: function() - { - let shellSvc = getShellService(); - let setDefaultPane = document.getElementById("setDefaultPane"); - if (!shellSvc) { - setDefaultPane.hidden = true; - document.getElementById("alwaysCheckDefault").disabled = true; - return; - } - let selectedIndex = - shellSvc.isDefaultBrowser(false, true) ? 1 : 0; - setDefaultPane.selectedIndex = selectedIndex; - }, - - /** - * Set browser as the operating system default browser. - */ - setDefaultBrowser: function() - { - let shellSvc = getShellService(); - if (!shellSvc) - return; - try { - let claimAllTypes = true; -#ifdef XP_WIN - // In Windows 8+, the UI for selecting default protocol is much - // nicer than the UI for setting file type associations. So we - // only show the protocol association screen on Windows 8+. - // Windows 8 is version 6.2. - let version = Services.sysinfo.getProperty("version"); - claimAllTypes = (parseFloat(version) < 6.2); -#endif - shellSvc.setDefaultBrowser(claimAllTypes, false); - } catch (ex) { - Cu.reportError(ex); - return; - } - let selectedIndex = - shellSvc.isDefaultBrowser(false, true) ? 1 : 0; - document.getElementById("setDefaultPane").selectedIndex = selectedIndex; - } -#endif -}; diff --git a/components/preferences/advanced.xul b/components/preferences/advanced.xul deleted file mode 100644 index e5f3bb1..0000000 --- a/components/preferences/advanced.xul +++ /dev/null @@ -1,465 +0,0 @@ -<?xml version="1.0"?> - -# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -<!DOCTYPE overlay [ -<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> -%brandDTD; -<!ENTITY % advancedDTD SYSTEM "chrome://browser/locale/preferences/advanced.dtd"> -%advancedDTD; -<!ENTITY % privacyDTD SYSTEM "chrome://browser/locale/preferences/privacy.dtd"> -%privacyDTD; -]> - -<overlay id="AdvancedPaneOverlay" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - - <prefpane id="paneAdvanced" onpaneload="gAdvancedPane.init();"> - - <preferences id="advancedPreferences"> - <preference id="browser.preferences.advanced.selectedTabIndex" - name="browser.preferences.advanced.selectedTabIndex" - type="int"/> - - <!--XXX button prefs --> - - <!-- General tab --> - <preference id="accessibility.typeaheadfind" name="accessibility.typeaheadfind" type="bool"/> - - <preference id="general.autoScroll" name="general.autoScroll" type="bool"/> - <preference id="general.smoothScroll" name="general.smoothScroll" type="bool"/> - <preference id="layers.acceleration.disabled" name="layers.acceleration.disabled" type="bool" inverted="true" - onchange="gAdvancedPane.updateHardwareAcceleration()"/> -#ifdef XP_WIN - <preference id="gfx.direct2d.disabled" name="gfx.direct2d.disabled" type="bool" inverted="true"/> -#endif - <preference id="layout.spellcheckDefault" name="layout.spellcheckDefault" type="int"/> - -#ifdef HAVE_SHELL_SERVICE - <preference id="browser.shell.checkDefaultBrowser" - name="browser.shell.checkDefaultBrowser" - type="bool"/> - - <preference id="pref.general.disable_button.default_browser" - name="pref.general.disable_button.default_browser" - type="bool"/> -#endif - <preference id="pref.general.compatmode" name="general.useragent.compatMode" type="int"/> - - <preference id="pref.general.captiveportal" name="network.captive-portal-service.enabled" type="bool"/> - - <!-- Network tab --> - <preference id="browser.cache.disk.capacity" name="browser.cache.disk.capacity" type="int"/> - - <preference id="browser.cache.disk.smart_size.enabled" - name="browser.cache.disk.smart_size.enabled" - inverted="true" - type="bool"/> - - <preference id="offline-apps.permissions" name="offline-apps.permissions" type="int" - onchange="gAdvancedPane.updateOfflineAppsPermissions()"/> - <preference id="browser.offline-apps.notify" name="browser.offline-apps.notify" type="bool"/> - <preference id="offline-apps.allow_by_default" name="offline-apps.allow_by_default" type="bool"/> - - <!-- Update tab --> -#ifdef MOZ_UPDATER - <preference id="app.update.enabled" name="app.update.enabled" type="bool"/> - <preference id="app.update.auto" name="app.update.auto" type="bool"/> - <preference id="app.update.mode" name="app.update.mode" type="int"/> - - <preference id="app.update.disable_button.showUpdateHistory" - name="app.update.disable_button.showUpdateHistory" - type="bool"/> -#endif - - <preference id="browser.search.update" name="browser.search.update" type="bool"/> - - <!-- Certificates tab --> - <preference id="security.default_personal_cert" name="security.default_personal_cert" type="string"/> - - <preference id="security.disable_button.openCertManager" - name="security.disable_button.openCertManager" - type="bool"/> - <preference id="security.disable_button.openDeviceManager" - name="security.disable_button.openDeviceManager" - type="bool"/> - <preference id="security.OCSP.enabled" - name="security.OCSP.enabled" - type="int"/> - <preference id="security.OCSP.require" - name="security.OCSP.require" - type="bool"/> - - <!-- Pale Moon: smooth scrolling tab --> - <preference id="general.smoothScroll.lines" name="general.smoothScroll.lines" type="bool"/> - <preference id="general.smoothScroll.lines.durationMinMS" name="general.smoothScroll.lines.durationMinMS" type="int"/> - <preference id="general.smoothScroll.lines.durationMaxMS" name="general.smoothScroll.lines.durationMaxMS" type="int"/> - <preference id="general.smoothScroll.pages" name="general.smoothScroll.pages" type="bool"/> - <preference id="general.smoothScroll.pages.durationMinMS" name="general.smoothScroll.pages.durationMinMS" type="int"/> - <preference id="general.smoothScroll.pages.durationMaxMS" name="general.smoothScroll.pages.durationMaxMS" type="int"/> - <preference id="general.smoothScroll.mouseWheel" name="general.smoothScroll.mouseWheel" type="bool"/> - <preference id="general.smoothScroll.mouseWheel.durationMinMS" name="general.smoothScroll.mouseWheel.durationMinMS" type="int"/> - <preference id="general.smoothScroll.mouseWheel.durationMaxMS" name="general.smoothScroll.mouseWheel.durationMaxMS" type="int"/> - <preference id="general.smoothScroll.scrollbars" name="general.smoothScroll.scrollbars" type="bool"/> - <preference id="general.smoothScroll.scrollbars.durationMinMS" name="general.smoothScroll.scrollbars.durationMinMS" type="int"/> - <preference id="general.smoothScroll.scrollbars.durationMaxMS" name="general.smoothScroll.scrollbars.durationMaxMS" type="int"/> - - <preference id="mousewheel.default.delta_multiplier_y" name="mousewheel.default.delta_multiplier_y" type="int"/> - </preferences> - -#ifdef HAVE_SHELL_SERVICE - <stringbundle id="bundleShell" src="chrome://browser/locale/shellservice.properties"/> - <stringbundle id="bundleBrand" src="chrome://branding/locale/brand.properties"/> -#endif - <stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/> - - <script type="application/javascript" src="chrome://browser/content/preferences/advanced.js"/> - - <tabbox id="advancedPrefs" flex="1" - onselect="gAdvancedPane.tabSelectionChanged();"> - - <tabs id="tabsElement"> - <tab id="generalTab" label="&generalTab.label;" helpTopic="prefs-advanced-general"/> - <tab id="networkTab" label="&networkTab.label;" helpTopic="prefs-advanced-network"/> - <tab id="updateTab" label="&updateTab.label;" helpTopic="prefs-advanced-update"/> - <tab id="encryptionTab" label="&certificateTab.label;" helpTopic="prefs-advanced-encryption"/> - <tab id="scrollparamTab" label="&scrollparamTab.label;" helpTopic="prefs-advanced-scrollparams"/> - </tabs> - - <tabpanels flex="1"> - - <!-- General --> - <tabpanel id="generalPanel" orient="vertical"> - - <!-- Accessibility --> - <groupbox id="accessibilityGroup" align="start"> - <caption label="&accessibility.label;"/> - - <checkbox id="searchStartTyping" - label="&searchStartTyping.label;" - accesskey="&searchStartTyping.accesskey;" - preference="accessibility.typeaheadfind"/> - </groupbox> - - <!-- Browsing --> - <groupbox id="browsingGroup" align="start"> - <caption label="&browsing.label;"/> - - <checkbox id="useAutoScroll" - label="&useAutoScroll.label;" - accesskey="&useAutoScroll.accesskey;" - preference="general.autoScroll"/> - <checkbox id="allowHWAccel" - label="&allowHWAccel.label;" - accesskey="&allowHWAccel.accesskey;" - preference="layers.acceleration.disabled"/> - <checkbox id="checkSpelling" - label="&checkSpelling.label;" - accesskey="&checkSpelling.accesskey;" - onsyncfrompreference="return gAdvancedPane.readCheckSpelling();" - onsynctopreference="return gAdvancedPane.writeCheckSpelling();" - preference="layout.spellcheckDefault"/> - </groupbox> - -#ifdef HAVE_SHELL_SERVICE - <!-- System Defaults --> - <groupbox id="systemDefaultsGroup" orient="vertical"> - <caption label="&systemDefaults.label;"/> - - <checkbox id="alwaysCheckDefault" preference="browser.shell.checkDefaultBrowser" - label="&alwaysCheckDefault.label;" accesskey="&alwaysCheckDefault.accesskey;" - flex="1"/> - <hbox class="indent"> - <deck id="setDefaultPane"> - <button id="setDefaultButton" - label="&setDefault.label;" accesskey="&setDefault.accesskey;" - oncommand="gAdvancedPane.setDefaultBrowser();" - preference="pref.general.disable_button.default_browser"/> - <description>&isDefault.label;</description> - </deck> - </hbox> - </groupbox> -#endif - <!-- User Agent compatibility --> - <groupbox id="UACompatGroup" orient="vertical"> - <caption label="&UACompatGroup.label;"/> - <hbox align="center"> - <label id="UACompat" control="UACompat-menu">&UACompat.label;</label> - <menulist id="UACompat-menu" preference="pref.general.compatmode" sizetopopup="always"> - <menupopup> - <menuitem label="&UACompat.Native;" value="0" /> - <menuitem label="&UACompat.Gecko;" value="1" /> - <menuitem label="&UACompat.Firefox;" value="2" /> - </menupopup> - </menulist> - </hbox> - </groupbox> - - <!-- Captive portal detection --> - <groupbox id="captivePortalGroup" orient="vertical"> - <caption label="&captivePortalGroup.label;"/> - <checkbox id="captivePortalDetect" - label="&captivePortalDetect.label;" - preference="pref.general.captiveportal"/> - </groupbox> - - </tabpanel> - - <!-- Network --> - <tabpanel id="networkPanel" orient="vertical"> - - <!-- Connection --> - <groupbox id="connectionGroup"> - <caption label="&connection.label;"/> - - <hbox align="center"> - <description flex="1" control="connectionSettings">&connectionDesc.label;</description> - <button id="connectionSettings" icon="network" label="&connectionSettings.label;" - accesskey="&connectionSettings.accesskey;" - oncommand="gAdvancedPane.showConnections();"/> - </hbox> - </groupbox> - - <!-- Cache --> - <groupbox id="cacheGroup"> - <caption label="&httpCache.label;"/> - - <hbox align="center"> - <label id="actualDiskCacheSize" flex="1"/> - <button id="clearCacheButton" icon="clear" - label="&clearCacheNow.label;" accesskey="&clearCacheNow.accesskey;" - oncommand="gAdvancedPane.clearCache();"/> - </hbox> - <checkbox preference="browser.cache.disk.smart_size.enabled" - id="allowSmartSize" flex="1" - onsyncfrompreference="return gAdvancedPane.readSmartSizeEnabled();" - label="&overrideSmartCacheSize.label;" - accesskey="&overrideSmartCacheSize.accesskey;"/> - <hbox align="center" class="indent"> - <label id="useCacheBefore" control="cacheSize" - accesskey="&limitCacheSizeBefore.accesskey;" - value="&limitCacheSizeBefore.label;"/> - <textbox id="cacheSize" type="number" size="4" max="1024" - preference="browser.cache.disk.capacity" - onsyncfrompreference="return gAdvancedPane.readCacheSize();" - onsynctopreference="return gAdvancedPane.writeCacheSize();" - aria-labelledby="useCacheBefore cacheSize useCacheAfter"/> - <label id="useCacheAfter" flex="1">&limitCacheSizeAfter.label;</label> - </hbox> - </groupbox> - - <!-- Offline apps --> - <groupbox id="offlineGroup"> - <caption label="&offlineStorage2.label;"/> - - <hbox align="center"> - <label id="actualAppCacheSize" flex="1"/> - <button id="clearOfflineAppCacheButton" icon="clear" - label="&clearOfflineAppCacheNow.label;" accesskey="&clearOfflineAppCacheNow.accesskey;" - oncommand="gAdvancedPane.clearOfflineAppCache();"/> - </hbox> - <label id="offlineAppsPermsLabel">&offlineAppsPermissions.label;</label> - <hbox align="center"> - <menulist id="offlineAppsPerms-menu" preference="offline-apps.permissions" sizetopopup="always"> - <menupopup> - <menuitem label="&offlineAppsPermissions.Allow;" value="2" /> - <menuitem label="&offlineAppsPermissions.Ask;" value="1" /> - <menuitem label="&offlineAppsPermissions.Deny;" value="0" /> - </menupopup> - </menulist> - <spacer flex="1"/> - <button id="offlineNotifyExceptions" - label="&offlineNotifyExceptions.label;" - accesskey="&offlineNotifyExceptions.accesskey;" - oncommand="gAdvancedPane.showOfflineExceptions();"/> - </hbox> - <hbox> - <vbox flex="1"> - <label id="offlineAppsListLabel">&offlineAppsList2.label;</label> - <listbox id="offlineAppsList" - style="height: &offlineAppsList.height;;" - flex="1" - aria-labelledby="offlineAppsListLabel" - onselect="gAdvancedPane.offlineAppSelected(event);"> - </listbox> - </vbox> - <vbox pack="end"> - <button id="offlineAppsListRemove" - disabled="true" - label="&offlineAppsListRemove.label;" - accesskey="&offlineAppsListRemove.accesskey;" - oncommand="gAdvancedPane.removeOfflineApp();"/> - </vbox> - </hbox> - </groupbox> - </tabpanel> - - <!-- Update --> - <tabpanel id="updatePanel" orient="vertical"> -#ifdef MOZ_UPDATER - <groupbox id="updateApp"> - <caption label="&updateApp.label;"/> - <radiogroup id="updateRadioGroup" - oncommand="gAdvancedPane.updateWritePrefs();"> - <radio id="autoDesktop" - value="auto" - label="&updateAuto1.label;" - accesskey="&updateAuto1.accesskey;"/> - <hbox class="indent"> - <checkbox id="warnIncompatible" - label="&updateAutoAddonWarn.label;" - accesskey="&updateAutoAddonWarn.accesskey;" - preference="app.update.mode" - onsyncfrompreference="return gAdvancedPane.readAddonWarn();" - onsynctopreference="return gAdvancedPane.writeAddonWarn();"/> - </hbox> - <radio value="checkOnly" - label="&updateCheck.label;" - accesskey="&updateCheck.accesskey;"/> - <radio value="manual" - label="&updateManual.label;" - accesskey="&updateManual.accesskey;"/> - </radiogroup> - - <hbox> - <button id="showUpdateHistory" - label="&updateHistory.label;" - accesskey="&updateHistory.accesskey;" - preference="app.update.disable_button.showUpdateHistory" - oncommand="gAdvancedPane.showUpdates();"/> - </hbox> - </groupbox> -#endif - <groupbox id="updateOthers"> - <caption label="&updateOthers.label;"/> - <checkbox id="enableSearchUpdate" - label="&enableSearchUpdate.label;" - accesskey="&enableSearchUpdate.accesskey;" - preference="browser.search.update"/> - </groupbox> - </tabpanel> - - <!-- Certificates --> - <tabpanel id="encryptionPanel" orient="vertical"> - - <!-- - The values on these radio buttons may look like l12y issues, but - they're not - this preference uses *those strings* as its values. - I KID YOU NOT. - --> - - <groupbox> - <caption label="&certGroup.label;"/> - <description id="CertSelectionDesc" control="certSelection">&certSelection.description;</description> - <radiogroup id="certSelection" orient="horizontal" preftype="string" - preference="security.default_personal_cert" - aria-labelledby="CertSelectionDesc"> - <radio label="&certs.auto;" accesskey="&certs.auto.accesskey;" - value="Select Automatically"/> - <radio label="&certs.ask;" accesskey="&certs.ask.accesskey;" - value="Ask Every Time"/> - </radiogroup> - </groupbox> - <groupbox> - <caption label="&ocspGroup.label;"/> - <checkbox id="enableOCSP" - label="&enableOCSP.label;" - accesskey="&enableOCSP.accesskey;" - onsyncfrompreference="return gAdvancedPane.readEnableOCSP();" - onsynctopreference="return gAdvancedPane.writeEnableOCSP();" - preference="security.OCSP.enabled"/> - <checkbox id="requireOCSP" - label="&requireOCSP.label;" - accesskey="&requireOCSP.accesskey;" - preference="security.OCSP.require"/> - </groupbox> - - <separator/> - - <hbox> - <button id="viewCertificatesButton" - label="&viewCerts.label;" accesskey="&viewCerts.accesskey;" - oncommand="gAdvancedPane.showCertificates();" - preference="security.disable_button.openCertManager"/> - <button id="viewSecurityDevicesButton" - label="&viewSecurityDevices.label;" accesskey="&viewSecurityDevices.accesskey;" - oncommand="gAdvancedPane.showSecurityDevices();" - preference="security.disable_button.openDeviceManager"/> - </hbox> - </tabpanel> - - <!-- Pale Moon: Scrolling tab --> - <tabpanel id="scrollparamTab" orient="vertical"> - - <checkbox id="useSmoothScrolling" - label="&useSmoothScrolling.label;" - accesskey="&useSmoothScrolling.accesskey;" - preference="general.smoothScroll"/> - - <label>&smoothscroll.explain.label;</label> - - <groupbox> - <caption label="&smoothscroll.params.label;"/> - - <checkbox label="&smoothscroll.mousewheel.label;" preference="general.smoothScroll.mouseWheel"/> - <hbox align="center" class="indent"> - <label value="&smoothscroll.mousewheel.duration;"/> - <textbox type="number" size="3" max="500" - preference="general.smoothScroll.mouseWheel.durationMinMS"/> - <label>&smoothscroll.to;</label> - <textbox type="number" size="4" max="2000" - preference="general.smoothScroll.mouseWheel.durationMaxMS"/> - <label flex="1">ms.</label> - </hbox> - - <checkbox label="&smoothscroll.arrowkeys.label;" preference="general.smoothScroll.lines"/> - <hbox align="center" class="indent"> - <label value="&smoothscroll.arrowkeys.duration;"/> - <textbox type="number" size="3" max="500" - preference="general.smoothScroll.lines.durationMinMS"/> - <label>&smoothscroll.to;</label> - <textbox type="number" size="4" max="2000" - preference="general.smoothScroll.lines.durationMaxMS"/> - <label flex="1">ms.</label> - </hbox> - - <checkbox label="&smoothscroll.pagekeys.label;" preference="general.smoothScroll.pages"/> - <hbox align="center" class="indent"> - <label value="&smoothscroll.pagekeys.duration;"/> - <textbox type="number" size="3" max="500" - preference="general.smoothScroll.pages.durationMinMS"/> - <label>&smoothscroll.to;</label> - <textbox type="number" size="4" max="2000" - preference="general.smoothScroll.pages.durationMaxMS"/> - <label flex="1">ms.</label> - </hbox> - - <checkbox label="&smoothscroll.scrollbar.label;" preference="general.smoothScroll.scrollbars"/> - <hbox align="center" class="indent"> - <label value="&smoothscroll.scrollbar.duration;"/> - <textbox type="number" size="3" max="500" - preference="general.smoothScroll.scrollbars.durationMinMS"/> - <label>&smoothscroll.to;</label> - <textbox type="number" size="4" max="2000" - preference="general.smoothScroll.scrollbars.durationMaxMS"/> - <label flex="1">ms.</label> - </hbox> - - <hbox align="center"> - <label value="&smoothscroll.overall.yspeed.label;"/> - <textbox type="number" size="3" min="1" max="999" - preference="mousewheel.default.delta_multiplier_y"/> - <label flex="1">%.</label> - </hbox> - </groupbox> - </tabpanel> - <!-- end Smooth scrolling tab --> - - </tabpanels> - </tabbox> - </prefpane> - -</overlay> diff --git a/components/preferences/applicationManager.js b/components/preferences/applicationManager.js deleted file mode 100644 index 5213183..0000000 --- a/components/preferences/applicationManager.js +++ /dev/null @@ -1,102 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#ifdef XP_MACOSX -var Cc = Components.classes; -var Ci = Components.interfaces; -#endif - -var gAppManagerDialog = { - _removed: [], - - init: function appManager_init() { - this.handlerInfo = window.arguments[0]; - - var bundle = document.getElementById("appManagerBundle"); - var contentText; - if (this.handlerInfo.type == TYPE_MAYBE_FEED) - contentText = bundle.getString("handleWebFeeds"); - else { - var description = gApplicationsPane._describeType(this.handlerInfo); - var key = - (this.handlerInfo.wrappedHandlerInfo instanceof Ci.nsIMIMEInfo) ? "handleFile" - : "handleProtocol"; - contentText = bundle.getFormattedString(key, [description]); - } - contentText = bundle.getFormattedString("descriptionApplications", [contentText]); - document.getElementById("appDescription").textContent = contentText; - - var list = document.getElementById("appList"); - var apps = this.handlerInfo.possibleApplicationHandlers.enumerate(); - while (apps.hasMoreElements()) { - let app = apps.getNext(); - if (!gApplicationsPane.isValidHandlerApp(app)) - continue; - - app.QueryInterface(Ci.nsIHandlerApp); - var item = list.appendItem(app.name); - item.setAttribute("image", gApplicationsPane._getIconURLForHandlerApp(app)); - item.className = "listitem-iconic"; - item.app = app; - } - - list.selectedIndex = 0; - }, - - onOK: function appManager_onOK() { - if (!this._removed.length) { - // return early to avoid calling the |store| method. - return; - } - - for (var i = 0; i < this._removed.length; ++i) - this.handlerInfo.removePossibleApplicationHandler(this._removed[i]); - - this.handlerInfo.store(); - }, - - onCancel: function appManager_onCancel() { - // do nothing - }, - - remove: function appManager_remove() { - var list = document.getElementById("appList"); - this._removed.push(list.selectedItem.app); - var index = list.selectedIndex; - list.removeItemAt(index); - if (list.getRowCount() == 0) { - // The list is now empty, make the bottom part disappear - document.getElementById("appDetails").hidden = true; - } - else { - // Select the item at the same index, if we removed the last - // item of the list, select the previous item - if (index == list.getRowCount()) - --index; - list.selectedIndex = index; - } - }, - - onSelect: function appManager_onSelect() { - var list = document.getElementById("appList"); - if (!list.selectedItem) { - document.getElementById("remove").disabled = true; - return; - } - document.getElementById("remove").disabled = false; - var app = list.selectedItem.app; - var address = ""; - if (app instanceof Ci.nsILocalHandlerApp) - address = app.executable.path; - else if (app instanceof Ci.nsIWebHandlerApp) - address = app.uriTemplate; - else if (app instanceof Ci.nsIWebContentHandlerInfo) - address = app.uri; - document.getElementById("appLocation").value = address; - var bundle = document.getElementById("appManagerBundle"); - var appType = app instanceof Ci.nsILocalHandlerApp ? "descriptionLocalApp" - : "descriptionWebApp"; - document.getElementById("appType").value = bundle.getString(appType); - } -}; diff --git a/components/preferences/applicationManager.xul b/components/preferences/applicationManager.xul deleted file mode 100644 index b5605c2..0000000 --- a/components/preferences/applicationManager.xul +++ /dev/null @@ -1,59 +0,0 @@ -<?xml version="1.0"?> -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<?xml-stylesheet href="chrome://global/skin/"?> - -<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/applicationManager.dtd"> - -<dialog id="appManager" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - buttons="accept,cancel" - onload="gAppManagerDialog.init();" - ondialogaccept="gAppManagerDialog.onOK();" - ondialogcancel="gAppManagerDialog.onCancel();" - title="&appManager.title;" - style="&appManager.style;" - persist="screenX screenY"> - - <script type="application/javascript" - src="chrome://browser/content/utilityOverlay.js"/> - <script type="application/javascript" - src="chrome://browser/content/preferences/applicationManager.js"/> - <script type="application/javascript" - src="chrome://browser/content/preferences/applications.js"/> - - <commandset id="appManagerCommandSet"> - <command id="cmd_remove" - oncommand="gAppManagerDialog.remove();" - disabled="true"/> - </commandset> - - <keyset id="appManagerKeyset"> - <key id="delete" keycode="VK_DELETE" command="cmd_remove"/> - </keyset> - - <stringbundleset id="appManagerBundleset"> - <stringbundle id="appManagerBundle" - src="chrome://browser/locale/preferences/applicationManager.properties"/> - </stringbundleset> - - <description id="appDescription"/> - <separator class="thin"/> - <hbox flex="1"> - <listbox id="appList" onselect="gAppManagerDialog.onSelect();" flex="1"/> - <vbox> - <button id="remove" - label="&remove.label;" - accesskey="&remove.accesskey;" - command="cmd_remove"/> - <spacer flex="1"/> - </vbox> - </hbox> - <vbox id="appDetails"> - <separator class="thin"/> - <label id="appType"/> - <textbox id="appLocation" readonly="true" class="plain"/> - </vbox> -</dialog> diff --git a/components/preferences/applications.js b/components/preferences/applications.js deleted file mode 100644 index d06f9f9..0000000 --- a/components/preferences/applications.js +++ /dev/null @@ -1,1890 +0,0 @@ -/* -# -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -//****************************************************************************// -// Constants & Enumeration Values - -var Cc = Components.classes; -var Ci = Components.interfaces; -var Cr = Components.results; - -Components.utils.import('resource://gre/modules/Services.jsm'); - -const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed"; -const TYPE_MAYBE_VIDEO_FEED = "application/vnd.mozilla.maybe.video.feed"; -const TYPE_MAYBE_AUDIO_FEED = "application/vnd.mozilla.maybe.audio.feed"; - -const PREF_DISABLED_PLUGIN_TYPES = "plugin.disable_full_page_plugin_for_types"; - -// Preferences that affect which entries to show in the list. -const PREF_SHOW_PLUGINS_IN_LIST = "browser.download.show_plugins_in_list"; -const PREF_HIDE_PLUGINS_WITHOUT_EXTENSIONS = - "browser.download.hide_plugins_without_extensions"; - -/* - * Preferences where we store handling information about the feed type. - * - * browser.feeds.handler - * - "bookmarks", "reader" (clarified further using the .default preference), - * or "ask" -- indicates the default handler being used to process feeds; - * "bookmarks" is obsolete; to specify that the handler is bookmarks, - * set browser.feeds.handler.default to "bookmarks"; - * - * browser.feeds.handler.default - * - "bookmarks", "client" or "web" -- indicates the chosen feed reader used - * to display feeds, either transiently (i.e., when the "use as default" - * checkbox is unchecked, corresponds to when browser.feeds.handler=="ask") - * or more permanently (i.e., the item displayed in the dropdown in Feeds - * preferences) - * - * browser.feeds.handler.webservice - * - the URL of the currently selected web service used to read feeds - * - * browser.feeds.handlers.application - * - nsILocalFile, stores the current client-side feed reading app if one has - * been chosen - */ -const PREF_FEED_SELECTED_APP = "browser.feeds.handlers.application"; -const PREF_FEED_SELECTED_WEB = "browser.feeds.handlers.webservice"; -const PREF_FEED_SELECTED_ACTION = "browser.feeds.handler"; -const PREF_FEED_SELECTED_READER = "browser.feeds.handler.default"; - -const PREF_VIDEO_FEED_SELECTED_APP = "browser.videoFeeds.handlers.application"; -const PREF_VIDEO_FEED_SELECTED_WEB = "browser.videoFeeds.handlers.webservice"; -const PREF_VIDEO_FEED_SELECTED_ACTION = "browser.videoFeeds.handler"; -const PREF_VIDEO_FEED_SELECTED_READER = "browser.videoFeeds.handler.default"; - -const PREF_AUDIO_FEED_SELECTED_APP = "browser.audioFeeds.handlers.application"; -const PREF_AUDIO_FEED_SELECTED_WEB = "browser.audioFeeds.handlers.webservice"; -const PREF_AUDIO_FEED_SELECTED_ACTION = "browser.audioFeeds.handler"; -const PREF_AUDIO_FEED_SELECTED_READER = "browser.audioFeeds.handler.default"; - -// The nsHandlerInfoAction enumeration values in nsIHandlerInfo identify -// the actions the application can take with content of various types. -// But since nsIHandlerInfo doesn't support plugins, there's no value -// identifying the "use plugin" action, so we use this constant instead. -const kActionUsePlugin = 5; - -/* -#ifdef MOZ_WIDGET_GTK -*/ -const ICON_URL_APP = "moz-icon://dummy.exe?size=16"; -/* -#else -*/ -const ICON_URL_APP = "chrome://browser/skin/preferences/application.png"; -/* -#endif -*/ - -// For CSS. Can be one of "ask", "save", "plugin" or "feed". If absent, the icon URL -// was set by us to a custom handler icon and CSS should not try to override it. -const APP_ICON_ATTR_NAME = "appHandlerIcon"; - -//****************************************************************************// -// Utilities - -function getFileDisplayName(file) { -#ifdef XP_WIN - if (file instanceof Ci.nsILocalFileWin) { - try { - return file.getVersionInfoField("FileDescription"); - } catch (e) {} - } -#endif -#ifdef XP_MACOSX - if (file instanceof Ci.nsILocalFileMac) { - try { - return file.bundleDisplayName; - } catch (e) {} - } -#endif - return file.leafName; -} - -function getLocalHandlerApp(aFile) { - var localHandlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"]. - createInstance(Ci.nsILocalHandlerApp); - localHandlerApp.name = getFileDisplayName(aFile); - localHandlerApp.executable = aFile; - - return localHandlerApp; -} - -/** - * An enumeration of items in a JS array. - * - * FIXME: use ArrayConverter once it lands (bug 380839). - * - * @constructor - */ -function ArrayEnumerator(aItems) { - this._index = 0; - this._contents = aItems; -} - -ArrayEnumerator.prototype = { - _index: 0, - - hasMoreElements: function() { - return this._index < this._contents.length; - }, - - getNext: function() { - return this._contents[this._index++]; - } -}; - -function isFeedType(t) { - return t == TYPE_MAYBE_FEED || t == TYPE_MAYBE_VIDEO_FEED || t == TYPE_MAYBE_AUDIO_FEED; -} - -//****************************************************************************// -// HandlerInfoWrapper - -/** - * This object wraps nsIHandlerInfo with some additional functionality - * the Applications prefpane needs to display and allow modification of - * the list of handled types. - * - * We create an instance of this wrapper for each entry we might display - * in the prefpane, and we compose the instances from various sources, - * including plugins and the handler service. - * - * We don't implement all the original nsIHandlerInfo functionality, - * just the stuff that the prefpane needs. - * - * In theory, all of the custom functionality in this wrapper should get - * pushed down into nsIHandlerInfo eventually. - */ -function HandlerInfoWrapper(aType, aHandlerInfo) { - this._type = aType; - this.wrappedHandlerInfo = aHandlerInfo; -} - -HandlerInfoWrapper.prototype = { - // The wrapped nsIHandlerInfo object. In general, this object is private, - // but there are a couple cases where callers access it directly for things - // we haven't (yet?) implemented, so we make it a public property. - wrappedHandlerInfo: null, - - - //**************************************************************************// - // Convenience Utils - - _handlerSvc: Cc["@mozilla.org/uriloader/handler-service;1"]. - getService(Ci.nsIHandlerService), - - _prefSvc: Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefBranch), - - _categoryMgr: Cc["@mozilla.org/categorymanager;1"]. - getService(Ci.nsICategoryManager), - - element: function(aID) { - return document.getElementById(aID); - }, - - - //**************************************************************************// - // nsIHandlerInfo - - // The MIME type or protocol scheme. - _type: null, - get type() { - return this._type; - }, - - get description() { - if (this.wrappedHandlerInfo.description) - return this.wrappedHandlerInfo.description; - - if (this.primaryExtension) { - var extension = this.primaryExtension.toUpperCase(); - return this.element("bundlePreferences").getFormattedString("fileEnding", - [extension]); - } - - return this.type; - }, - - get preferredApplicationHandler() { - return this.wrappedHandlerInfo.preferredApplicationHandler; - }, - - set preferredApplicationHandler(aNewValue) { - this.wrappedHandlerInfo.preferredApplicationHandler = aNewValue; - - // Make sure the preferred handler is in the set of possible handlers. - if (aNewValue) - this.addPossibleApplicationHandler(aNewValue) - }, - - get possibleApplicationHandlers() { - return this.wrappedHandlerInfo.possibleApplicationHandlers; - }, - - addPossibleApplicationHandler: function(aNewHandler) { - var possibleApps = this.possibleApplicationHandlers.enumerate(); - while (possibleApps.hasMoreElements()) { - if (possibleApps.getNext().equals(aNewHandler)) - return; - } - this.possibleApplicationHandlers.appendElement(aNewHandler, false); - }, - - removePossibleApplicationHandler: function(aHandler) { - var defaultApp = this.preferredApplicationHandler; - if (defaultApp && aHandler.equals(defaultApp)) { - // If the app we remove was the default app, we must make sure - // it won't be used anymore - this.alwaysAskBeforeHandling = true; - this.preferredApplicationHandler = null; - } - - var handlers = this.possibleApplicationHandlers; - for (var i = 0; i < handlers.length; ++i) { - var handler = handlers.queryElementAt(i, Ci.nsIHandlerApp); - if (handler.equals(aHandler)) { - handlers.removeElementAt(i); - break; - } - } - }, - - get hasDefaultHandler() { - return this.wrappedHandlerInfo.hasDefaultHandler; - }, - - get defaultDescription() { - return this.wrappedHandlerInfo.defaultDescription; - }, - - // What to do with content of this type. - get preferredAction() { - // If we have an enabled plugin, then the action is to use that plugin. - if (this.pluginName && !this.isDisabledPluginType) - return kActionUsePlugin; - - // If the action is to use a helper app, but we don't have a preferred - // handler app, then switch to using the system default, if any; otherwise - // fall back to saving to disk, which is the default action in nsMIMEInfo. - // Note: "save to disk" is an invalid value for protocol info objects, - // but the alwaysAskBeforeHandling getter will detect that situation - // and always return true in that case to override this invalid value. - if (this.wrappedHandlerInfo.preferredAction == Ci.nsIHandlerInfo.useHelperApp && - !gApplicationsPane.isValidHandlerApp(this.preferredApplicationHandler)) { - if (this.wrappedHandlerInfo.hasDefaultHandler) - return Ci.nsIHandlerInfo.useSystemDefault; - else - return Ci.nsIHandlerInfo.saveToDisk; - } - - return this.wrappedHandlerInfo.preferredAction; - }, - - set preferredAction(aNewValue) { - // If the action is to use the plugin, - // we must set the preferred action to "save to disk". - // But only if it's not currently the preferred action. - if ((aNewValue == kActionUsePlugin) && - (this.preferredAction != Ci.nsIHandlerInfo.saveToDisk)) { - aNewValue = Ci.nsIHandlerInfo.saveToDisk; - } - - // We don't modify the preferred action if the new action is to use a plugin - // because handler info objects don't understand our custom "use plugin" - // value. Also, leaving it untouched means that we can automatically revert - // to the old setting if the user ever removes the plugin. - - if (aNewValue != kActionUsePlugin) - this.wrappedHandlerInfo.preferredAction = aNewValue; - }, - - get alwaysAskBeforeHandling() { - // If this type is handled only by a plugin, we can't trust the value - // in the handler info object, since it'll be a default based on the absence - // of any user configuration, and the default in that case is to always ask, - // even though we never ask for content handled by a plugin, so special case - // plugin-handled types by returning false here. - if (this.pluginName && this.handledOnlyByPlugin) - return false; - - // If this is a protocol type and the preferred action is "save to disk", - // which is invalid for such types, then return true here to override that - // action. This could happen when the preferred action is to use a helper - // app, but the preferredApplicationHandler is invalid, and there isn't - // a default handler, so the preferredAction getter returns save to disk - // instead. - if (!(this.wrappedHandlerInfo instanceof Ci.nsIMIMEInfo) && - this.preferredAction == Ci.nsIHandlerInfo.saveToDisk) - return true; - - return this.wrappedHandlerInfo.alwaysAskBeforeHandling; - }, - - set alwaysAskBeforeHandling(aNewValue) { - this.wrappedHandlerInfo.alwaysAskBeforeHandling = aNewValue; - }, - - - //**************************************************************************// - // nsIMIMEInfo - - // The primary file extension associated with this type, if any. - // - // XXX Plugin objects contain an array of MimeType objects with "suffixes" - // properties; if this object has an associated plugin, shouldn't we check - // those properties for an extension? - get primaryExtension() { - try { - if (this.wrappedHandlerInfo instanceof Ci.nsIMIMEInfo && - this.wrappedHandlerInfo.primaryExtension) - return this.wrappedHandlerInfo.primaryExtension - } catch(ex) {} - - return null; - }, - - - //**************************************************************************// - // Plugin Handling - - // A plugin that can handle this type, if any. - // - // Note: just because we have one doesn't mean it *will* handle the type. - // That depends on whether or not the type is in the list of types for which - // plugin handling is disabled. - plugin: null, - - // Whether or not this type is only handled by a plugin or is also handled - // by some user-configured action as specified in the handler info object. - // - // Note: we can't just check if there's a handler info object for this type, - // because OS and user configuration is mixed up in the handler info object, - // so we always need to retrieve it for the OS info and can't tell whether - // it represents only OS-default information or user-configured information. - // - // FIXME: once handler info records are broken up into OS-provided records - // and user-configured records, stop using this boolean flag and simply - // check for the presence of a user-configured record to determine whether - // or not this type is only handled by a plugin. Filed as bug 395142. - handledOnlyByPlugin: undefined, - - get isDisabledPluginType() { - return this._getDisabledPluginTypes().indexOf(this.type) != -1; - }, - - _getDisabledPluginTypes: function() { - var types = ""; - - if (this._prefSvc.prefHasUserValue(PREF_DISABLED_PLUGIN_TYPES)) - types = this._prefSvc.getCharPref(PREF_DISABLED_PLUGIN_TYPES); - - // Only split if the string isn't empty so we don't end up with an array - // containing a single empty string. - if (types != "") - return types.split(","); - - return []; - }, - - disablePluginType: function() { - var disabledPluginTypes = this._getDisabledPluginTypes(); - - if (disabledPluginTypes.indexOf(this.type) == -1) - disabledPluginTypes.push(this.type); - - this._prefSvc.setCharPref(PREF_DISABLED_PLUGIN_TYPES, - disabledPluginTypes.join(",")); - - // Update the category manager so existing browser windows update. - this._categoryMgr.deleteCategoryEntry("Goanna-Content-Viewers", - this.type, - false); - }, - - enablePluginType: function() { - var disabledPluginTypes = this._getDisabledPluginTypes(); - - var type = this.type; - disabledPluginTypes = disabledPluginTypes.filter(function(v) v != type); - - this._prefSvc.setCharPref(PREF_DISABLED_PLUGIN_TYPES, - disabledPluginTypes.join(",")); - - // Update the category manager so existing browser windows update. - this._categoryMgr. - addCategoryEntry("Goanna-Content-Viewers", - this.type, - "@mozilla.org/content/plugin/document-loader-factory;1", - false, - true); - }, - - - //**************************************************************************// - // Storage - - store: function() { - this._handlerSvc.store(this.wrappedHandlerInfo); - }, - - - //**************************************************************************// - // Icons - - get smallIcon() { - return this._getIcon(16); - }, - - _getIcon: function(aSize) { - if (this.primaryExtension) - return "moz-icon://goat." + this.primaryExtension + "?size=" + aSize; - - if (this.wrappedHandlerInfo instanceof Ci.nsIMIMEInfo) - return "moz-icon://goat?size=" + aSize + "&contentType=" + this.type; - - // FIXME: consider returning some generic icon when we can't get a URL for - // one (for example in the case of protocol schemes). Filed as bug 395141. - return null; - } - -}; - - -//****************************************************************************// -// Feed Handler Info - -/** - * This object implements nsIHandlerInfo for the feed types. It's a separate - * object because we currently store handling information for the feed type - * in a set of preferences rather than the nsIHandlerService-managed datastore. - * - * This object inherits from HandlerInfoWrapper in order to get functionality - * that isn't special to the feed type. - * - * XXX Should we inherit from HandlerInfoWrapper? After all, we override - * most of that wrapper's properties and methods, and we have to dance around - * the fact that the wrapper expects to have a wrappedHandlerInfo, which we - * don't provide. - */ - -function FeedHandlerInfo(aMIMEType) { - HandlerInfoWrapper.call(this, aMIMEType, null); -} - -FeedHandlerInfo.prototype = { - __proto__: HandlerInfoWrapper.prototype, - - //**************************************************************************// - // Convenience Utils - - _converterSvc: - Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"]. - getService(Ci.nsIWebContentConverterService), - - _shellSvc: -#ifdef HAVE_SHELL_SERVICE - getShellService(), -#else - null, -#endif - - - //**************************************************************************// - // nsIHandlerInfo - - get description() { - return this.element("bundlePreferences").getString(this._appPrefLabel); - }, - - get preferredApplicationHandler() { - switch (this.element(this._prefSelectedReader).value) { - case "client": - var file = this.element(this._prefSelectedApp).value; - if (file) - return getLocalHandlerApp(file); - - return null; - - case "web": - var uri = this.element(this._prefSelectedWeb).value; - if (!uri) - return null; - return this._converterSvc.getWebContentHandlerByURI(this.type, uri); - - case "bookmarks": - default: - // When the pref is set to bookmarks, we handle feeds internally, - // we don't forward them to a local or web handler app, so there is - // no preferred handler. - return null; - } - }, - - set preferredApplicationHandler(aNewValue) { - if (aNewValue instanceof Ci.nsILocalHandlerApp) { - this.element(this._prefSelectedApp).value = aNewValue.executable; - this.element(this._prefSelectedReader).value = "client"; - } - else if (aNewValue instanceof Ci.nsIWebContentHandlerInfo) { - this.element(this._prefSelectedWeb).value = aNewValue.uri; - this.element(this._prefSelectedReader).value = "web"; - // Make the web handler be the new "auto handler" for feeds. - // Note: we don't have to unregister the auto handler when the user picks - // a non-web handler (local app, Live Bookmarks, etc.) because the service - // only uses the "auto handler" when the selected reader is a web handler. - // We also don't have to unregister it when the user turns on "always ask" - // (i.e. preview in browser), since that also overrides the auto handler. - this._converterSvc.setAutoHandler(this.type, aNewValue); - } - }, - - _possibleApplicationHandlers: null, - - get possibleApplicationHandlers() { - if (this._possibleApplicationHandlers) - return this._possibleApplicationHandlers; - - // A minimal implementation of nsIMutableArray. It only supports the two - // methods its callers invoke, namely appendElement and nsIArray::enumerate. - this._possibleApplicationHandlers = { - _inner: [], - _removed: [], - - QueryInterface: function(aIID) { - if (aIID.equals(Ci.nsIMutableArray) || - aIID.equals(Ci.nsIArray) || - aIID.equals(Ci.nsISupports)) - return this; - - throw Cr.NS_ERROR_NO_INTERFACE; - }, - - get length() { - return this._inner.length; - }, - - enumerate: function() { - return new ArrayEnumerator(this._inner); - }, - - appendElement: function(aHandlerApp, aWeak) { - this._inner.push(aHandlerApp); - }, - - removeElementAt: function(aIndex) { - this._removed.push(this._inner[aIndex]); - this._inner.splice(aIndex, 1); - }, - - queryElementAt: function(aIndex, aInterface) { - return this._inner[aIndex].QueryInterface(aInterface); - } - }; - - // Add the selected local app if it's different from the OS default handler. - // Unlike for other types, we can store only one local app at a time for the - // feed type, since we store it in a preference that historically stores - // only a single path. But we display all the local apps the user chooses - // while the prefpane is open, only dropping the list when the user closes - // the prefpane, for maximum usability and consistency with other types. - var preferredAppFile = this.element(this._prefSelectedApp).value; - if (preferredAppFile) { - let preferredApp = getLocalHandlerApp(preferredAppFile); - let defaultApp = this._defaultApplicationHandler; - if (!defaultApp || !defaultApp.equals(preferredApp)) - this._possibleApplicationHandlers.appendElement(preferredApp, false); - } - - // Add the registered web handlers. There can be any number of these. - var webHandlers = this._converterSvc.getContentHandlers(this.type); - for each (let webHandler in webHandlers) - this._possibleApplicationHandlers.appendElement(webHandler, false); - - return this._possibleApplicationHandlers; - }, - - __defaultApplicationHandler: undefined, - get _defaultApplicationHandler() { - if (typeof this.__defaultApplicationHandler != "undefined") - return this.__defaultApplicationHandler; - - var defaultFeedReader = null; -#ifdef HAVE_SHELL_SERVICE - try { - defaultFeedReader = this._shellSvc.defaultFeedReader; - } - catch(ex) { - // no default reader or _shellSvc is null - } -#endif - - if (defaultFeedReader) { - let handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"]. - createInstance(Ci.nsIHandlerApp); - handlerApp.name = getFileDisplayName(defaultFeedReader); - handlerApp.QueryInterface(Ci.nsILocalHandlerApp); - handlerApp.executable = defaultFeedReader; - - this.__defaultApplicationHandler = handlerApp; - } - else { - this.__defaultApplicationHandler = null; - } - - return this.__defaultApplicationHandler; - }, - - get hasDefaultHandler() { -#ifdef HAVE_SHELL_SERVICE - try { - if (this._shellSvc.defaultFeedReader) - return true; - } - catch(ex) { - // no default reader or _shellSvc is null - } -#endif - - return false; - }, - - get defaultDescription() { - if (this.hasDefaultHandler) - return this._defaultApplicationHandler.name; - - // Should we instead return null? - return ""; - }, - - // What to do with content of this type. - get preferredAction() { - switch (this.element(this._prefSelectedAction).value) { - - case "bookmarks": - return Ci.nsIHandlerInfo.handleInternally; - - case "reader": { - let preferredApp = this.preferredApplicationHandler; - let defaultApp = this._defaultApplicationHandler; - - // If we have a valid preferred app, return useSystemDefault if it's - // the default app; otherwise return useHelperApp. - if (gApplicationsPane.isValidHandlerApp(preferredApp)) { - if (defaultApp && defaultApp.equals(preferredApp)) - return Ci.nsIHandlerInfo.useSystemDefault; - - return Ci.nsIHandlerInfo.useHelperApp; - } - - // The pref is set to "reader", but we don't have a valid preferred app. - // What do we do now? Not sure this is the best option (perhaps we - // should direct the user to the default app, if any), but for now let's - // direct the user to live bookmarks. - return Ci.nsIHandlerInfo.handleInternally; - } - - // If the action is "ask", then alwaysAskBeforeHandling will override - // the action, so it doesn't matter what we say it is, it just has to be - // something that doesn't cause the controller to hide the type. - case "ask": - default: - return Ci.nsIHandlerInfo.handleInternally; - } - }, - - set preferredAction(aNewValue) { - switch (aNewValue) { - - case Ci.nsIHandlerInfo.handleInternally: - this.element(this._prefSelectedReader).value = "bookmarks"; - break; - - case Ci.nsIHandlerInfo.useHelperApp: - this.element(this._prefSelectedAction).value = "reader"; - // The controller has already set preferredApplicationHandler - // to the new helper app. - break; - - case Ci.nsIHandlerInfo.useSystemDefault: - this.element(this._prefSelectedAction).value = "reader"; - this.preferredApplicationHandler = this._defaultApplicationHandler; - break; - } - }, - - get alwaysAskBeforeHandling() { - return this.element(this._prefSelectedAction).value == "ask"; - }, - - set alwaysAskBeforeHandling(aNewValue) { - if (aNewValue == true) - this.element(this._prefSelectedAction).value = "ask"; - else - this.element(this._prefSelectedAction).value = "reader"; - }, - - // Whether or not we are currently storing the action selected by the user. - // We use this to suppress notification-triggered updates to the list when - // we make changes that may spawn such updates, specifically when we change - // the action for the feed type, which results in feed preference updates, - // which spawn "pref changed" notifications that would otherwise cause us - // to rebuild the view unnecessarily. - _storingAction: false, - - - //**************************************************************************// - // nsIMIMEInfo - - get primaryExtension() { - return "xml"; - }, - - - //**************************************************************************// - // Storage - - // Changes to the preferred action and handler take effect immediately - // (we write them out to the preferences right as they happen), - // so we when the controller calls store() after modifying the handlers, - // the only thing we need to store is the removal of possible handlers - // XXX Should we hold off on making the changes until this method gets called? - store: function() { - for each (let app in this._possibleApplicationHandlers._removed) { - if (app instanceof Ci.nsILocalHandlerApp) { - let pref = this.element(PREF_FEED_SELECTED_APP); - var preferredAppFile = pref.value; - if (preferredAppFile) { - let preferredApp = getLocalHandlerApp(preferredAppFile); - if (app.equals(preferredApp)) - pref.reset(); - } - } - else { - app.QueryInterface(Ci.nsIWebContentHandlerInfo); - this._converterSvc.removeContentHandler(app.contentType, app.uri); - } - } - this._possibleApplicationHandlers._removed = []; - }, - - - //**************************************************************************// - // Icons - - get smallIcon() { - return this._smallIcon; - } - -}; - -var feedHandlerInfo = { - __proto__: new FeedHandlerInfo(TYPE_MAYBE_FEED), - _prefSelectedApp: PREF_FEED_SELECTED_APP, - _prefSelectedWeb: PREF_FEED_SELECTED_WEB, - _prefSelectedAction: PREF_FEED_SELECTED_ACTION, - _prefSelectedReader: PREF_FEED_SELECTED_READER, - _smallIcon: "chrome://browser/skin/feeds/feedIcon16.png", - _appPrefLabel: "webFeed" -} - -var videoFeedHandlerInfo = { - __proto__: new FeedHandlerInfo(TYPE_MAYBE_VIDEO_FEED), - _prefSelectedApp: PREF_VIDEO_FEED_SELECTED_APP, - _prefSelectedWeb: PREF_VIDEO_FEED_SELECTED_WEB, - _prefSelectedAction: PREF_VIDEO_FEED_SELECTED_ACTION, - _prefSelectedReader: PREF_VIDEO_FEED_SELECTED_READER, - _smallIcon: "chrome://browser/skin/feeds/videoFeedIcon16.png", - _appPrefLabel: "videoPodcastFeed" -} - -var audioFeedHandlerInfo = { - __proto__: new FeedHandlerInfo(TYPE_MAYBE_AUDIO_FEED), - _prefSelectedApp: PREF_AUDIO_FEED_SELECTED_APP, - _prefSelectedWeb: PREF_AUDIO_FEED_SELECTED_WEB, - _prefSelectedAction: PREF_AUDIO_FEED_SELECTED_ACTION, - _prefSelectedReader: PREF_AUDIO_FEED_SELECTED_READER, - _smallIcon: "chrome://browser/skin/feeds/audioFeedIcon16.png", - _appPrefLabel: "audioPodcastFeed" -} - -/** - * InternalHandlerInfoWrapper provides a basic mechanism to create an internal - * mime type handler that can be enabled/disabled in the applications preference - * menu. - */ -function InternalHandlerInfoWrapper(aMIMEType) { - var mimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); - var handlerInfo = mimeSvc.getFromTypeAndExtension(aMIMEType, null); - - HandlerInfoWrapper.call(this, aMIMEType, handlerInfo); -} - -InternalHandlerInfoWrapper.prototype = { - __proto__: HandlerInfoWrapper.prototype, - - // Override store so we so we can notify any code listening for registration - // or unregistration of this handler. - store: function() { - HandlerInfoWrapper.prototype.store.call(this); - Services.obs.notifyObservers(null, this._handlerChanged, null); - }, - - get enabled() { - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - }, - - get description() { - return this.element("bundlePreferences").getString(this._appPrefLabel); - } -}; - -//****************************************************************************// -// Prefpane Controller - -var gApplicationsPane = { - // The set of types the app knows how to handle. A hash of HandlerInfoWrapper - // objects, indexed by type. - _handledTypes: {}, - - // The list of types we can show, sorted by the sort column/direction. - // An array of HandlerInfoWrapper objects. We build this list when we first - // load the data and then rebuild it when users change a pref that affects - // what types we can show or change the sort column/direction. - // Note: this isn't necessarily the list of types we *will* show; if the user - // provides a filter string, we'll only show the subset of types in this list - // that match that string. - _visibleTypes: [], - - // A count of the number of times each visible type description appears. - // We use these counts to determine whether or not to annotate descriptions - // with their types to distinguish duplicate descriptions from each other. - // A hash of integer counts, indexed by string description. - _visibleTypeDescriptionCount: {}, - - - //**************************************************************************// - // Convenience & Performance Shortcuts - - // These get defined by init(). - _brandShortName : null, - _prefsBundle : null, - _list : null, - _filter : null, - - _prefSvc : Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefBranch), - - _mimeSvc : Cc["@mozilla.org/mime;1"]. - getService(Ci.nsIMIMEService), - - _helperAppSvc : Cc["@mozilla.org/uriloader/external-helper-app-service;1"]. - getService(Ci.nsIExternalHelperAppService), - - _handlerSvc : Cc["@mozilla.org/uriloader/handler-service;1"]. - getService(Ci.nsIHandlerService), - - _ioSvc : Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService), - - - //**************************************************************************// - // Initialization & Destruction - - init: function() { - // Initialize shortcuts to some commonly accessed elements & values. - this._brandShortName = - document.getElementById("bundleBrand").getString("brandShortName"); - this._prefsBundle = document.getElementById("bundlePreferences"); - this._list = document.getElementById("handlersView"); - this._filter = document.getElementById("filter"); - - // Observe preferences that influence what we display so we can rebuild - // the view when they change. - this._prefSvc.addObserver(PREF_SHOW_PLUGINS_IN_LIST, this, false); - this._prefSvc.addObserver(PREF_HIDE_PLUGINS_WITHOUT_EXTENSIONS, this, false); - this._prefSvc.addObserver(PREF_FEED_SELECTED_APP, this, false); - this._prefSvc.addObserver(PREF_FEED_SELECTED_WEB, this, false); - this._prefSvc.addObserver(PREF_FEED_SELECTED_ACTION, this, false); - this._prefSvc.addObserver(PREF_FEED_SELECTED_READER, this, false); - - this._prefSvc.addObserver(PREF_VIDEO_FEED_SELECTED_APP, this, false); - this._prefSvc.addObserver(PREF_VIDEO_FEED_SELECTED_WEB, this, false); - this._prefSvc.addObserver(PREF_VIDEO_FEED_SELECTED_ACTION, this, false); - this._prefSvc.addObserver(PREF_VIDEO_FEED_SELECTED_READER, this, false); - - this._prefSvc.addObserver(PREF_AUDIO_FEED_SELECTED_APP, this, false); - this._prefSvc.addObserver(PREF_AUDIO_FEED_SELECTED_WEB, this, false); - this._prefSvc.addObserver(PREF_AUDIO_FEED_SELECTED_ACTION, this, false); - this._prefSvc.addObserver(PREF_AUDIO_FEED_SELECTED_READER, this, false); - - - // Listen for window unload so we can remove our preference observers. - window.addEventListener("unload", this, false); - - // Figure out how we should be sorting the list. We persist sort settings - // across sessions, so we can't assume the default sort column/direction. - // XXX should we be using the XUL sort service instead? - if (document.getElementById("actionColumn").hasAttribute("sortDirection")) { - this._sortColumn = document.getElementById("actionColumn"); - // The typeColumn element always has a sortDirection attribute, - // either because it was persisted or because the default value - // from the xul file was used. If we are sorting on the other - // column, we should remove it. - document.getElementById("typeColumn").removeAttribute("sortDirection"); - } - else - this._sortColumn = document.getElementById("typeColumn"); - - // Load the data and build the list of handlers. - // By doing this in a timeout, we let the preferences dialog resize itself - // to an appropriate size before we add a bunch of items to the list. - // Otherwise, if there are many items, and the Applications prefpane - // is the one that gets displayed when the user first opens the dialog, - // the dialog might stretch too much in an attempt to fit them all in. - // XXX Shouldn't we perhaps just set a max-height on the richlistbox? - var _delayedPaneLoad = function(self) { - self._loadData(); - self._rebuildVisibleTypes(); - self._sortVisibleTypes(); - self._rebuildView(); - - // Notify observers that the UI is now ready - Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService). - notifyObservers(window, "app-handler-pane-loaded", null); - } - setTimeout(_delayedPaneLoad, 0, this); - }, - - destroy: function() { - window.removeEventListener("unload", this, false); - this._prefSvc.removeObserver(PREF_SHOW_PLUGINS_IN_LIST, this); - this._prefSvc.removeObserver(PREF_HIDE_PLUGINS_WITHOUT_EXTENSIONS, this); - this._prefSvc.removeObserver(PREF_FEED_SELECTED_APP, this); - this._prefSvc.removeObserver(PREF_FEED_SELECTED_WEB, this); - this._prefSvc.removeObserver(PREF_FEED_SELECTED_ACTION, this); - this._prefSvc.removeObserver(PREF_FEED_SELECTED_READER, this); - - this._prefSvc.removeObserver(PREF_VIDEO_FEED_SELECTED_APP, this); - this._prefSvc.removeObserver(PREF_VIDEO_FEED_SELECTED_WEB, this); - this._prefSvc.removeObserver(PREF_VIDEO_FEED_SELECTED_ACTION, this); - this._prefSvc.removeObserver(PREF_VIDEO_FEED_SELECTED_READER, this); - - this._prefSvc.removeObserver(PREF_AUDIO_FEED_SELECTED_APP, this); - this._prefSvc.removeObserver(PREF_AUDIO_FEED_SELECTED_WEB, this); - this._prefSvc.removeObserver(PREF_AUDIO_FEED_SELECTED_ACTION, this); - this._prefSvc.removeObserver(PREF_AUDIO_FEED_SELECTED_READER, this); - }, - - - //**************************************************************************// - // nsISupports - - QueryInterface: function(aIID) { - if (aIID.equals(Ci.nsIObserver) || - aIID.equals(Ci.nsIDOMEventListener || - aIID.equals(Ci.nsISupports))) - return this; - - throw Cr.NS_ERROR_NO_INTERFACE; - }, - - - //**************************************************************************// - // nsIObserver - - observe: function (aSubject, aTopic, aData) { - // Rebuild the list when there are changes to preferences that influence - // whether or not to show certain entries in the list. - if (aTopic == "nsPref:changed" && !this._storingAction) { - // These two prefs alter the list of visible types, so we have to rebuild - // that list when they change. - if (aData == PREF_SHOW_PLUGINS_IN_LIST || - aData == PREF_HIDE_PLUGINS_WITHOUT_EXTENSIONS) { - this._rebuildVisibleTypes(); - this._sortVisibleTypes(); - } - - // All the prefs we observe can affect what we display, so we rebuild - // the view when any of them changes. - this._rebuildView(); - } - }, - - - //**************************************************************************// - // nsIDOMEventListener - - handleEvent: function(aEvent) { - if (aEvent.type == "unload") { - this.destroy(); - } - }, - - - //**************************************************************************// - // Composed Model Construction - - _loadData: function() { - this._loadFeedHandler(); - this._loadPluginHandlers(); - this._loadApplicationHandlers(); - }, - - _loadFeedHandler: function() { - this._handledTypes[TYPE_MAYBE_FEED] = feedHandlerInfo; - feedHandlerInfo.handledOnlyByPlugin = false; - - this._handledTypes[TYPE_MAYBE_VIDEO_FEED] = videoFeedHandlerInfo; - videoFeedHandlerInfo.handledOnlyByPlugin = false; - - this._handledTypes[TYPE_MAYBE_AUDIO_FEED] = audioFeedHandlerInfo; - audioFeedHandlerInfo.handledOnlyByPlugin = false; - }, - - /** - * Load the set of handlers defined by plugins. - * - * Note: if there's more than one plugin for a given MIME type, we assume - * the last one is the one that the application will use. That may not be - * correct, but it's how we've been doing it for years. - * - * Perhaps we should instead query navigator.mimeTypes for the set of types - * supported by the application and then get the plugin from each MIME type's - * enabledPlugin property. But if there's a plugin for a type, we need - * to know about it even if it isn't enabled, since we're going to give - * the user an option to enable it. - * - * Also note that enabledPlugin does not get updated when - * plugin.disable_full_page_plugin_for_types changes, so even if we could use - * enabledPlugin to get the plugin that would be used, we'd still need to - * check the pref ourselves to find out if it's enabled. - */ - _loadPluginHandlers: function() { - "use strict"; - - let mimeTypes = navigator.mimeTypes; - - for (let mimeType of mimeTypes) { - let handlerInfoWrapper; - if (mimeType.type in this._handledTypes) { - handlerInfoWrapper = this._handledTypes[mimeType.type]; - } else { - let wrappedHandlerInfo = - this._mimeSvc.getFromTypeAndExtension(mimeType.type, null); - handlerInfoWrapper = new HandlerInfoWrapper(mimeType.type, wrappedHandlerInfo); - handlerInfoWrapper.handledOnlyByPlugin = true; - this._handledTypes[mimeType.type] = handlerInfoWrapper; - } - handlerInfoWrapper.pluginName = mimeType.enabledPlugin.name; - } - }, - - /** - * Load the set of handlers defined by the application datastore. - */ - _loadApplicationHandlers: function() { - var wrappedHandlerInfos = this._handlerSvc.enumerate(); - while (wrappedHandlerInfos.hasMoreElements()) { - let wrappedHandlerInfo = - wrappedHandlerInfos.getNext().QueryInterface(Ci.nsIHandlerInfo); - let type = wrappedHandlerInfo.type; - - let handlerInfoWrapper; - if (type in this._handledTypes) - handlerInfoWrapper = this._handledTypes[type]; - else { - handlerInfoWrapper = new HandlerInfoWrapper(type, wrappedHandlerInfo); - this._handledTypes[type] = handlerInfoWrapper; - } - - handlerInfoWrapper.handledOnlyByPlugin = false; - } - }, - - - //**************************************************************************// - // View Construction - - _rebuildVisibleTypes: function() { - // Reset the list of visible types and the visible type description counts. - this._visibleTypes = []; - this._visibleTypeDescriptionCount = {}; - - // Get the preferences that help determine what types to show. - var showPlugins = this._prefSvc.getBoolPref(PREF_SHOW_PLUGINS_IN_LIST); - var hidePluginsWithoutExtensions = - this._prefSvc.getBoolPref(PREF_HIDE_PLUGINS_WITHOUT_EXTENSIONS); - - for (let type in this._handledTypes) { - let handlerInfo = this._handledTypes[type]; - - // Hide plugins without associated extensions if so prefed so we don't - // show a whole bunch of obscure types handled by plugins on Mac. - // Note: though protocol types don't have extensions, we still show them; - // the pref is only meant to be applied to MIME types, since plugins are - // only associated with MIME types. - // FIXME: should we also check the "suffixes" property of the plugin? - // Filed as bug 395135. - if (hidePluginsWithoutExtensions && handlerInfo.handledOnlyByPlugin && - handlerInfo.wrappedHandlerInfo instanceof Ci.nsIMIMEInfo && - !handlerInfo.primaryExtension) - continue; - - // Hide types handled only by plugins if so prefed. - if (handlerInfo.handledOnlyByPlugin && !showPlugins) - continue; - - // We couldn't find any reason to exclude the type, so include it. - this._visibleTypes.push(handlerInfo); - - if (handlerInfo.description in this._visibleTypeDescriptionCount) - this._visibleTypeDescriptionCount[handlerInfo.description]++; - else - this._visibleTypeDescriptionCount[handlerInfo.description] = 1; - } - }, - - _rebuildView: function() { - // Clear the list of entries. - while (this._list.childNodes.length > 1) - this._list.removeChild(this._list.lastChild); - - var visibleTypes = this._visibleTypes; - - // If the user is filtering the list, then only show matching types. - if (this._filter.value) - visibleTypes = visibleTypes.filter(this._matchesFilter, this); - - for each (let visibleType in visibleTypes) { - let item = document.createElement("richlistitem"); - item.setAttribute("type", visibleType.type); - item.setAttribute("typeDescription", this._describeType(visibleType)); - if (visibleType.smallIcon) - item.setAttribute("typeIcon", visibleType.smallIcon); - item.setAttribute("actionDescription", - this._describePreferredAction(visibleType)); - - if (!this._setIconClassForPreferredAction(visibleType, item)) { - item.setAttribute("actionIcon", - this._getIconURLForPreferredAction(visibleType)); - } - - this._list.appendChild(item); - } - - this._selectLastSelectedType(); - }, - - _matchesFilter: function(aType) { - var filterValue = this._filter.value.toLowerCase(); - return this._describeType(aType).toLowerCase().indexOf(filterValue) != -1 || - this._describePreferredAction(aType).toLowerCase().indexOf(filterValue) != -1; - }, - - /** - * Describe, in a human-readable fashion, the type represented by the given - * handler info object. Normally this is just the description provided by - * the info object, but if more than one object presents the same description, - * then we annotate the duplicate descriptions with the type itself to help - * users distinguish between those types. - * - * @param aHandlerInfo {nsIHandlerInfo} the type being described - * @returns {string} a description of the type - */ - _describeType: function(aHandlerInfo) { - if (this._visibleTypeDescriptionCount[aHandlerInfo.description] > 1) - return this._prefsBundle.getFormattedString("typeDescriptionWithType", - [aHandlerInfo.description, - aHandlerInfo.type]); - - return aHandlerInfo.description; - }, - - /** - * Describe, in a human-readable fashion, the preferred action to take on - * the type represented by the given handler info object. - * - * XXX Should this be part of the HandlerInfoWrapper interface? It would - * violate the separation of model and view, but it might make more sense - * nonetheless (f.e. it would make sortTypes easier). - * - * @param aHandlerInfo {nsIHandlerInfo} the type whose preferred action - * is being described - * @returns {string} a description of the action - */ - _describePreferredAction: function(aHandlerInfo) { - // alwaysAskBeforeHandling overrides the preferred action, so if that flag - // is set, then describe that behavior instead. For most types, this is - // the "alwaysAsk" string, but for the feed type we show something special. - if (aHandlerInfo.alwaysAskBeforeHandling) { - if (isFeedType(aHandlerInfo.type)) - return this._prefsBundle.getFormattedString("previewInApp", - [this._brandShortName]); - else - return this._prefsBundle.getString("alwaysAsk"); - } - - switch (aHandlerInfo.preferredAction) { - case Ci.nsIHandlerInfo.saveToDisk: - return this._prefsBundle.getString("saveFile"); - - case Ci.nsIHandlerInfo.useHelperApp: - var preferredApp = aHandlerInfo.preferredApplicationHandler; - var name; - if (preferredApp instanceof Ci.nsILocalHandlerApp) - name = getFileDisplayName(preferredApp.executable); - else - name = preferredApp.name; - return this._prefsBundle.getFormattedString("useApp", [name]); - - case Ci.nsIHandlerInfo.handleInternally: - // For the feed type, handleInternally means live bookmarks. - if (isFeedType(aHandlerInfo.type)) { - return this._prefsBundle.getFormattedString("addLiveBookmarksInApp", - [this._brandShortName]); - } - - if (aHandlerInfo instanceof InternalHandlerInfoWrapper) { - return this._prefsBundle.getFormattedString("previewInApp", - [this._brandShortName]); - } - - // For other types, handleInternally looks like either useHelperApp - // or useSystemDefault depending on whether or not there's a preferred - // handler app. - if (this.isValidHandlerApp(aHandlerInfo.preferredApplicationHandler)) - return aHandlerInfo.preferredApplicationHandler.name; - - return aHandlerInfo.defaultDescription; - - // XXX Why don't we say the app will handle the type internally? - // Is it because the app can't actually do that? But if that's true, - // then why would a preferredAction ever get set to this value - // in the first place? - - case Ci.nsIHandlerInfo.useSystemDefault: - return this._prefsBundle.getFormattedString("useDefault", - [aHandlerInfo.defaultDescription]); - - case kActionUsePlugin: - return this._prefsBundle.getFormattedString("usePluginIn", - [aHandlerInfo.pluginName, - this._brandShortName]); - } - }, - - _selectLastSelectedType: function() { - // If the list is disabled by the pref.downloads.disable_button.edit_actions - // preference being locked, then don't select the type, as that would cause - // it to appear selected, with a different background and an actions menu - // that makes it seem like you can choose an action for the type. - if (this._list.disabled) - return; - - var lastSelectedType = this._list.getAttribute("lastSelectedType"); - if (!lastSelectedType) - return; - - var item = this._list.getElementsByAttribute("type", lastSelectedType)[0]; - if (!item) - return; - - this._list.selectedItem = item; - }, - - /** - * Whether or not the given handler app is valid. - * - * @param aHandlerApp {nsIHandlerApp} the handler app in question - * - * @returns {boolean} whether or not it's valid - */ - isValidHandlerApp: function(aHandlerApp) { - if (!aHandlerApp) - return false; - - if (aHandlerApp instanceof Ci.nsILocalHandlerApp) - return this._isValidHandlerExecutable(aHandlerApp.executable); - - if (aHandlerApp instanceof Ci.nsIWebHandlerApp) - return aHandlerApp.uriTemplate; - - if (aHandlerApp instanceof Ci.nsIWebContentHandlerInfo) - return aHandlerApp.uri; - - return false; - }, - - _isValidHandlerExecutable: function(aExecutable) { - return aExecutable && - aExecutable.exists() && - aExecutable.isExecutable() && -// XXXben - we need to compare this with the running instance executable -// just don't know how to do that via script... -// XXXmano TBD: can probably add this to nsIShellService -#ifdef XP_WIN -#expand aExecutable.leafName != "__MOZ_APP_NAME__.exe"; -#else -#ifdef XP_MACOSX -#expand aExecutable.leafName != "__MOZ_MACBUNDLE_NAME__"; -#else -#expand aExecutable.leafName != "__MOZ_APP_NAME__-bin"; -#endif -#endif - }, - - /** - * Rebuild the actions menu for the selected entry. Gets called by - * the richlistitem constructor when an entry in the list gets selected. - */ - rebuildActionsMenu: function() { - var typeItem = this._list.selectedItem; - var handlerInfo = this._handledTypes[typeItem.type]; - var menu = - document.getAnonymousElementByAttribute(typeItem, "class", "actionsMenu"); - var menuPopup = menu.menupopup; - - // Clear out existing items. - while (menuPopup.hasChildNodes()) - menuPopup.removeChild(menuPopup.lastChild); - - // Add the "Preview in Firefox" option for optional internal handlers. - if (handlerInfo instanceof InternalHandlerInfoWrapper) { - var internalMenuItem = document.createElement("menuitem"); - internalMenuItem.setAttribute("action", Ci.nsIHandlerInfo.handleInternally); - let label = this._prefsBundle.getFormattedString("previewInApp", - [this._brandShortName]); - internalMenuItem.setAttribute("label", label); - internalMenuItem.setAttribute("tooltiptext", label); - internalMenuItem.setAttribute(APP_ICON_ATTR_NAME, "ask"); - menuPopup.appendChild(internalMenuItem); - } - - { - var askMenuItem = document.createElement("menuitem"); - askMenuItem.setAttribute("action", Ci.nsIHandlerInfo.alwaysAsk); - let label; - if (isFeedType(handlerInfo.type)) - label = this._prefsBundle.getFormattedString("previewInApp", - [this._brandShortName]); - else - label = this._prefsBundle.getString("alwaysAsk"); - askMenuItem.setAttribute("label", label); - askMenuItem.setAttribute("tooltiptext", label); - askMenuItem.setAttribute(APP_ICON_ATTR_NAME, "ask"); - menuPopup.appendChild(askMenuItem); - } - - // Create a menu item for saving to disk. - // Note: this option isn't available to protocol types, since we don't know - // what it means to save a URL having a certain scheme to disk, nor is it - // available to feeds, since the feed code doesn't implement the capability. - if ((handlerInfo.wrappedHandlerInfo instanceof Ci.nsIMIMEInfo) && - !isFeedType(handlerInfo.type)) { - var saveMenuItem = document.createElement("menuitem"); - saveMenuItem.setAttribute("action", Ci.nsIHandlerInfo.saveToDisk); - let label = this._prefsBundle.getString("saveFile"); - saveMenuItem.setAttribute("label", label); - saveMenuItem.setAttribute("tooltiptext", label); - saveMenuItem.setAttribute(APP_ICON_ATTR_NAME, "save"); - menuPopup.appendChild(saveMenuItem); - } - - // If this is the feed type, add a Live Bookmarks item. - if (isFeedType(handlerInfo.type)) { - var internalMenuItem = document.createElement("menuitem"); - internalMenuItem.setAttribute("action", Ci.nsIHandlerInfo.handleInternally); - let label = this._prefsBundle.getFormattedString("addLiveBookmarksInApp", - [this._brandShortName]); - internalMenuItem.setAttribute("label", label); - internalMenuItem.setAttribute("tooltiptext", label); - internalMenuItem.setAttribute(APP_ICON_ATTR_NAME, "feed"); - menuPopup.appendChild(internalMenuItem); - } - - // Add a separator to distinguish these items from the helper app items - // that follow them. - let menuItem = document.createElement("menuseparator"); - menuPopup.appendChild(menuItem); - - // Create a menu item for the OS default application, if any. - if (handlerInfo.hasDefaultHandler) { - var defaultMenuItem = document.createElement("menuitem"); - defaultMenuItem.setAttribute("action", Ci.nsIHandlerInfo.useSystemDefault); - let label = this._prefsBundle.getFormattedString("useDefault", - [handlerInfo.defaultDescription]); - defaultMenuItem.setAttribute("label", label); - defaultMenuItem.setAttribute("tooltiptext", handlerInfo.defaultDescription); - defaultMenuItem.setAttribute("image", this._getIconURLForSystemDefault(handlerInfo)); - - menuPopup.appendChild(defaultMenuItem); - } - - // Create menu items for possible handlers. - let preferredApp = handlerInfo.preferredApplicationHandler; - let possibleApps = handlerInfo.possibleApplicationHandlers.enumerate(); - var possibleAppMenuItems = []; - while (possibleApps.hasMoreElements()) { - let possibleApp = possibleApps.getNext(); - if (!this.isValidHandlerApp(possibleApp)) - continue; - - let menuItem = document.createElement("menuitem"); - menuItem.setAttribute("action", Ci.nsIHandlerInfo.useHelperApp); - let label; - if (possibleApp instanceof Ci.nsILocalHandlerApp) - label = getFileDisplayName(possibleApp.executable); - else - label = possibleApp.name; - label = this._prefsBundle.getFormattedString("useApp", [label]); - menuItem.setAttribute("label", label); - menuItem.setAttribute("tooltiptext", label); - menuItem.setAttribute("image", this._getIconURLForHandlerApp(possibleApp)); - - // Attach the handler app object to the menu item so we can use it - // to make changes to the datastore when the user selects the item. - menuItem.handlerApp = possibleApp; - - menuPopup.appendChild(menuItem); - possibleAppMenuItems.push(menuItem); - } - - // Create a menu item for the plugin. - if (handlerInfo.pluginName) { - var pluginMenuItem = document.createElement("menuitem"); - pluginMenuItem.setAttribute("action", kActionUsePlugin); - let label = this._prefsBundle.getFormattedString("usePluginIn", - [handlerInfo.pluginName, - this._brandShortName]); - pluginMenuItem.setAttribute("label", label); - pluginMenuItem.setAttribute("tooltiptext", label); - pluginMenuItem.setAttribute(APP_ICON_ATTR_NAME, "plugin"); - menuPopup.appendChild(pluginMenuItem); - } - - // Create a menu item for selecting a local application. -#ifdef XP_WIN - // On Windows, selecting an application to open another application - // would be meaningless so we special case executables. - var executableType = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService) - .getTypeFromExtension("exe"); - if (handlerInfo.type != executableType) -#endif - { - let menuItem = document.createElement("menuitem"); - menuItem.setAttribute("oncommand", "gApplicationsPane.chooseApp(event)"); - let label = this._prefsBundle.getString("useOtherApp"); - menuItem.setAttribute("label", label); - menuItem.setAttribute("tooltiptext", label); - menuPopup.appendChild(menuItem); - } - - // Create a menu item for managing applications. - if (possibleAppMenuItems.length) { - let menuItem = document.createElement("menuseparator"); - menuPopup.appendChild(menuItem); - menuItem = document.createElement("menuitem"); - menuItem.setAttribute("oncommand", "gApplicationsPane.manageApp(event)"); - menuItem.setAttribute("label", this._prefsBundle.getString("manageApp")); - menuPopup.appendChild(menuItem); - } - - // Select the item corresponding to the preferred action. If the always - // ask flag is set, it overrides the preferred action. Otherwise we pick - // the item identified by the preferred action (when the preferred action - // is to use a helper app, we have to pick the specific helper app item). - if (handlerInfo.alwaysAskBeforeHandling) - menu.selectedItem = askMenuItem; - else switch (handlerInfo.preferredAction) { - case Ci.nsIHandlerInfo.handleInternally: - menu.selectedItem = internalMenuItem; - break; - case Ci.nsIHandlerInfo.useSystemDefault: - menu.selectedItem = defaultMenuItem; - break; - case Ci.nsIHandlerInfo.useHelperApp: - if (preferredApp) - menu.selectedItem = - possibleAppMenuItems.filter(function(v) v.handlerApp.equals(preferredApp))[0]; - break; - case kActionUsePlugin: - menu.selectedItem = pluginMenuItem; - break; - case Ci.nsIHandlerInfo.saveToDisk: - menu.selectedItem = saveMenuItem; - break; - } - }, - - - //**************************************************************************// - // Sorting & Filtering - - _sortColumn: null, - - /** - * Sort the list when the user clicks on a column header. - */ - sort: function (event) { - var column = event.target; - - // If the user clicked on a new sort column, remove the direction indicator - // from the old column. - if (this._sortColumn && this._sortColumn != column) - this._sortColumn.removeAttribute("sortDirection"); - - this._sortColumn = column; - - // Set (or switch) the sort direction indicator. - if (column.getAttribute("sortDirection") == "ascending") - column.setAttribute("sortDirection", "descending"); - else - column.setAttribute("sortDirection", "ascending"); - - this._sortVisibleTypes(); - this._rebuildView(); - }, - - /** - * Sort the list of visible types by the current sort column/direction. - */ - _sortVisibleTypes: function() { - if (!this._sortColumn) - return; - - var t = this; - - function sortByType(a, b) { - return t._describeType(a).toLowerCase(). - localeCompare(t._describeType(b).toLowerCase()); - } - - function sortByAction(a, b) { - return t._describePreferredAction(a).toLowerCase(). - localeCompare(t._describePreferredAction(b).toLowerCase()); - } - - switch (this._sortColumn.getAttribute("value")) { - case "type": - this._visibleTypes.sort(sortByType); - break; - case "action": - this._visibleTypes.sort(sortByAction); - break; - } - - if (this._sortColumn.getAttribute("sortDirection") == "descending") - this._visibleTypes.reverse(); - }, - - /** - * Filter the list when the user enters a filter term into the filter field. - */ - filter: function() { - this._rebuildView(); - }, - - focusFilterBox: function() { - this._filter.focus(); - this._filter.select(); - }, - - - //**************************************************************************// - // Changes - - onSelectAction: function(aActionItem) { - this._storingAction = true; - - try { - this._storeAction(aActionItem); - } - finally { - this._storingAction = false; - } - }, - - _storeAction: function(aActionItem) { - var typeItem = this._list.selectedItem; - var handlerInfo = this._handledTypes[typeItem.type]; - - let action = parseInt(aActionItem.getAttribute("action")); - - // Set the plugin state if we're enabling or disabling a plugin. - if (action == kActionUsePlugin) - handlerInfo.enablePluginType(); - else if (handlerInfo.pluginName && !handlerInfo.isDisabledPluginType) - handlerInfo.disablePluginType(); - - // Set the preferred application handler. - // We leave the existing preferred app in the list when we set - // the preferred action to something other than useHelperApp so that - // legacy datastores that don't have the preferred app in the list - // of possible apps still include the preferred app in the list of apps - // the user can choose to handle the type. - if (action == Ci.nsIHandlerInfo.useHelperApp) - handlerInfo.preferredApplicationHandler = aActionItem.handlerApp; - - // Set the "always ask" flag. - if (action == Ci.nsIHandlerInfo.alwaysAsk) - handlerInfo.alwaysAskBeforeHandling = true; - else - handlerInfo.alwaysAskBeforeHandling = false; - - // Set the preferred action. - handlerInfo.preferredAction = action; - - handlerInfo.store(); - - // Make sure the handler info object is flagged to indicate that there is - // now some user configuration for the type. - handlerInfo.handledOnlyByPlugin = false; - - // Update the action label and image to reflect the new preferred action. - typeItem.setAttribute("actionDescription", - this._describePreferredAction(handlerInfo)); - if (!this._setIconClassForPreferredAction(handlerInfo, typeItem)) { - typeItem.setAttribute("actionIcon", - this._getIconURLForPreferredAction(handlerInfo)); - } - }, - - manageApp: function(aEvent) { - // Don't let the normal "on select action" handler get this event, - // as we handle it specially ourselves. - aEvent.stopPropagation(); - - var typeItem = this._list.selectedItem; - var handlerInfo = this._handledTypes[typeItem.type]; - - document.documentElement.openSubDialog("chrome://browser/content/preferences/applicationManager.xul", - "", handlerInfo); - - // Rebuild the actions menu so that we revert to the previous selection, - // or "Always ask" if the previous default application has been removed - this.rebuildActionsMenu(); - - // update the richlistitem too. Will be visible when selecting another row - typeItem.setAttribute("actionDescription", - this._describePreferredAction(handlerInfo)); - if (!this._setIconClassForPreferredAction(handlerInfo, typeItem)) { - typeItem.setAttribute("actionIcon", - this._getIconURLForPreferredAction(handlerInfo)); - } - }, - - chooseApp: function(aEvent) { - // Don't let the normal "on select action" handler get this event, - // as we handle it specially ourselves. - aEvent.stopPropagation(); - - var handlerApp; - let chooseAppCallback = function(aHandlerApp) { - // Rebuild the actions menu whether the user picked an app or canceled. - // If they picked an app, we want to add the app to the menu and select it. - // If they canceled, we want to go back to their previous selection. - this.rebuildActionsMenu(); - - // If the user picked a new app from the menu, select it. - if (aHandlerApp) { - let typeItem = this._list.selectedItem; - let actionsMenu = - document.getAnonymousElementByAttribute(typeItem, "class", "actionsMenu"); - let menuItems = actionsMenu.menupopup.childNodes; - for (let i = 0; i < menuItems.length; i++) { - let menuItem = menuItems[i]; - if (menuItem.handlerApp && menuItem.handlerApp.equals(aHandlerApp)) { - actionsMenu.selectedIndex = i; - this.onSelectAction(menuItem); - break; - } - } - } - }.bind(this); - -#ifdef XP_WIN - var params = {}; - var handlerInfo = this._handledTypes[this._list.selectedItem.type]; - - if (isFeedType(handlerInfo.type)) { - // MIME info will be null, create a temp object. - params.mimeInfo = this._mimeSvc.getFromTypeAndExtension(handlerInfo.type, - handlerInfo.primaryExtension); - } else { - params.mimeInfo = handlerInfo.wrappedHandlerInfo; - } - - params.title = this._prefsBundle.getString("fpTitleChooseApp"); - params.description = handlerInfo.description; - params.filename = null; - params.handlerApp = null; - - window.openDialog("chrome://global/content/appPicker.xul", null, - "chrome,modal,centerscreen,titlebar,dialog=yes", - params); - - if (this.isValidHandlerApp(params.handlerApp)) { - handlerApp = params.handlerApp; - - // Add the app to the type's list of possible handlers. - handlerInfo.addPossibleApplicationHandler(handlerApp); - } - - chooseAppCallback(handlerApp); -#else - let winTitle = this._prefsBundle.getString("fpTitleChooseApp"); - let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker); - let fpCallback = function fpCallback_done(aResult) { - if (aResult == Ci.nsIFilePicker.returnOK && fp.file && - this._isValidHandlerExecutable(fp.file)) { - handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"]. - createInstance(Ci.nsILocalHandlerApp); - handlerApp.name = getFileDisplayName(fp.file); - handlerApp.executable = fp.file; - - // Add the app to the type's list of possible handlers. - let handlerInfo = this._handledTypes[this._list.selectedItem.type]; - handlerInfo.addPossibleApplicationHandler(handlerApp); - - chooseAppCallback(handlerApp); - } - }.bind(this); - - // Prompt the user to pick an app. If they pick one, and it's a valid - // selection, then add it to the list of possible handlers. - fp.init(window, winTitle, Ci.nsIFilePicker.modeOpen); - fp.appendFilters(Ci.nsIFilePicker.filterApps); - fp.open(fpCallback); -#endif - }, - - // Mark which item in the list was last selected so we can reselect it - // when we rebuild the list or when the user returns to the prefpane. - onSelectionChanged: function() { - if (this._list.selectedItem) - this._list.setAttribute("lastSelectedType", - this._list.selectedItem.getAttribute("type")); - }, - - _setIconClassForPreferredAction: function(aHandlerInfo, aElement) { - // If this returns true, the attribute that CSS sniffs for was set to something - // so you shouldn't manually set an icon URI. - // This removes the existing actionIcon attribute if any, even if returning false. - aElement.removeAttribute("actionIcon"); - - if (aHandlerInfo.alwaysAskBeforeHandling) { - aElement.setAttribute(APP_ICON_ATTR_NAME, "ask"); - return true; - } - - switch (aHandlerInfo.preferredAction) { - case Ci.nsIHandlerInfo.saveToDisk: - aElement.setAttribute(APP_ICON_ATTR_NAME, "save"); - return true; - - case Ci.nsIHandlerInfo.handleInternally: - if (isFeedType(aHandlerInfo.type)) { - aElement.setAttribute(APP_ICON_ATTR_NAME, "feed"); - return true; - } else if (aHandlerInfo instanceof InternalHandlerInfoWrapper) { - aElement.setAttribute(APP_ICON_ATTR_NAME, "ask"); - return true; - } - break; - - case kActionUsePlugin: - aElement.setAttribute(APP_ICON_ATTR_NAME, "plugin"); - return true; - } - aElement.removeAttribute(APP_ICON_ATTR_NAME); - return false; - }, - - _getIconURLForPreferredAction: function(aHandlerInfo) { - switch (aHandlerInfo.preferredAction) { - case Ci.nsIHandlerInfo.useSystemDefault: - return this._getIconURLForSystemDefault(aHandlerInfo); - - case Ci.nsIHandlerInfo.useHelperApp: - let preferredApp = aHandlerInfo.preferredApplicationHandler; - if (this.isValidHandlerApp(preferredApp)) - return this._getIconURLForHandlerApp(preferredApp); - break; - - // This should never happen, but if preferredAction is set to some weird - // value, then fall back to the generic application icon. - default: - return ICON_URL_APP; - } - }, - - _getIconURLForHandlerApp: function(aHandlerApp) { - if (aHandlerApp instanceof Ci.nsILocalHandlerApp) - return this._getIconURLForFile(aHandlerApp.executable); - - if (aHandlerApp instanceof Ci.nsIWebHandlerApp) - return this._getIconURLForWebApp(aHandlerApp.uriTemplate); - - if (aHandlerApp instanceof Ci.nsIWebContentHandlerInfo) - return this._getIconURLForWebApp(aHandlerApp.uri) - - // We know nothing about other kinds of handler apps. - return ""; - }, - - _getIconURLForFile: function(aFile) { - var fph = this._ioSvc.getProtocolHandler("file"). - QueryInterface(Ci.nsIFileProtocolHandler); - var urlSpec = fph.getURLSpecFromFile(aFile); - - return "moz-icon://" + urlSpec + "?size=16"; - }, - - _getIconURLForWebApp: function(aWebAppURITemplate) { - var uri = this._ioSvc.newURI(aWebAppURITemplate, null, null); - - // Unfortunately we can't use the favicon service to get the favicon, - // because the service looks for a record with the exact URL we give it, and - // users won't have such records for URLs they don't visit, and users won't - // visit the handler's URL template, they'll only visit URLs derived from - // that template (i.e. with %s in the template replaced by the URL of the - // content being handled). - - if (/^https?$/.test(uri.scheme) && this._prefSvc.getBoolPref("browser.chrome.favicons")) - return uri.prePath + "/favicon.ico"; - - return ""; - }, - - _getIconURLForSystemDefault: function(aHandlerInfo) { - // Handler info objects for MIME types on some OSes implement a property bag - // interface from which we can get an icon for the default app, so if we're - // dealing with a MIME type on one of those OSes, then try to get the icon. - if ("wrappedHandlerInfo" in aHandlerInfo) { - let wrappedHandlerInfo = aHandlerInfo.wrappedHandlerInfo; - - if (wrappedHandlerInfo instanceof Ci.nsIMIMEInfo && - wrappedHandlerInfo instanceof Ci.nsIPropertyBag) { - try { - let url = wrappedHandlerInfo.getProperty("defaultApplicationIconURL"); - if (url) - return url + "?size=16"; - } - catch(ex) {} - } - } - - // If this isn't a MIME type object on an OS that supports retrieving - // the icon, or if we couldn't retrieve the icon for some other reason, - // then use a generic icon. - return ICON_URL_APP; - } - -}; diff --git a/components/preferences/applications.xul b/components/preferences/applications.xul deleted file mode 100644 index 2e6fa54..0000000 --- a/components/preferences/applications.xul +++ /dev/null @@ -1,99 +0,0 @@ -<?xml version="1.0"?> - -<!-- -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- --> -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<!DOCTYPE overlay [ - <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> - <!ENTITY % applicationsDTD SYSTEM "chrome://browser/locale/preferences/applications.dtd"> - %brandDTD; - %applicationsDTD; -]> - -<overlay id="ApplicationsPaneOverlay" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - - <prefpane id="paneApplications" - onpaneload="gApplicationsPane.init();" - flex="1" - helpTopic="prefs-applications"> - - <preferences id="feedsPreferences"> - <preference id="browser.feeds.handler" - name="browser.feeds.handler" - type="string"/> - <preference id="browser.feeds.handler.default" - name="browser.feeds.handler.default" - type="string"/> - <preference id="browser.feeds.handlers.application" - name="browser.feeds.handlers.application" - type="file"/> - <preference id="browser.feeds.handlers.webservice" - name="browser.feeds.handlers.webservice" - type="string"/> - - <preference id="browser.videoFeeds.handler" - name="browser.videoFeeds.handler" - type="string"/> - <preference id="browser.videoFeeds.handler.default" - name="browser.videoFeeds.handler.default" - type="string"/> - <preference id="browser.videoFeeds.handlers.application" - name="browser.videoFeeds.handlers.application" - type="file"/> - <preference id="browser.videoFeeds.handlers.webservice" - name="browser.videoFeeds.handlers.webservice" - type="string"/> - - <preference id="browser.audioFeeds.handler" - name="browser.audioFeeds.handler" - type="string"/> - <preference id="browser.audioFeeds.handler.default" - name="browser.audioFeeds.handler.default" - type="string"/> - <preference id="browser.audioFeeds.handlers.application" - name="browser.audioFeeds.handlers.application" - type="file"/> - <preference id="browser.audioFeeds.handlers.webservice" - name="browser.audioFeeds.handlers.webservice" - type="string"/> - - <preference id="pref.downloads.disable_button.edit_actions" - name="pref.downloads.disable_button.edit_actions" - type="bool"/> - </preferences> - - <script type="application/javascript" src="chrome://browser/content/preferences/applications.js"/> - - <keyset> - <key key="&focusSearch1.key;" modifiers="accel" oncommand="gApplicationsPane.focusFilterBox();"/> - <key key="&focusSearch2.key;" modifiers="accel" oncommand="gApplicationsPane.focusFilterBox();"/> - </keyset> - - <hbox> - <textbox id="filter" flex="1" - type="search" - placeholder="&filter.emptytext;" - aria-controls="handlersView" - oncommand="gApplicationsPane.filter();"/> - </hbox> - - <separator class="thin"/> - - <richlistbox id="handlersView" orient="vertical" persist="lastSelectedType" - preference="pref.downloads.disable_button.edit_actions" - onselect="gApplicationsPane.onSelectionChanged();"> - <listheader equalsize="always" style="border: 0; padding: 0; -moz-appearance: none;"> - <treecol id="typeColumn" label="&typeColumn.label;" value="type" - accesskey="&typeColumn.accesskey;" persist="sortDirection" - flex="1" onclick="gApplicationsPane.sort(event);" - sortDirection="ascending"/> - <treecol id="actionColumn" label="&actionColumn2.label;" value="action" - accesskey="&actionColumn2.accesskey;" persist="sortDirection" - flex="1" onclick="gApplicationsPane.sort(event);"/> - </listheader> - </richlistbox> - </prefpane> -</overlay> diff --git a/components/preferences/colors.xul b/components/preferences/colors.xul deleted file mode 100644 index f2109ae..0000000 --- a/components/preferences/colors.xul +++ /dev/null @@ -1,102 +0,0 @@ -<?xml version="1.0"?> - -# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> -#ifdef XP_MACOSX -<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?> -#endif - -<!DOCTYPE prefwindow SYSTEM "chrome://browser/locale/preferences/colors.dtd" > - -<prefwindow id="ColorsDialog" type="child" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - title="&colorsDialog.title;" - dlgbuttons="accept,cancel,help" - ondialoghelp="openPrefsHelp()" -#ifdef XP_MACOSX - style="width: &window.macWidth; !important;"> -#else - style="width: &window.width; !important;"> -#endif - - <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/> - <prefpane id="ColorsDialogPane" - helpTopic="prefs-fonts-and-colors"> - - <preferences> - <preference id="browser.display.document_color_use" name="browser.display.document_color_use" type="int"/> - <preference id="browser.anchor_color" name="browser.anchor_color" type="string"/> - <preference id="browser.visited_color" name="browser.visited_color" type="string"/> - <preference id="browser.underline_anchors" name="browser.underline_anchors" type="bool"/> - <preference id="browser.display.foreground_color" name="browser.display.foreground_color" type="string"/> - <preference id="browser.display.background_color" name="browser.display.background_color" type="string"/> - <preference id="browser.display.use_system_colors" name="browser.display.use_system_colors" type="bool"/> - </preferences> - - <hbox> - <groupbox flex="1"> - <caption label="&color;"/> - <hbox align="center"> - <label value="&textColor.label;" accesskey="&textColor.accesskey;" control="foregroundtextmenu"/> - <spacer flex="1"/> - <colorpicker type="button" id="foregroundtextmenu" palettename="standard" - preference="browser.display.foreground_color"/> - </hbox> - <hbox align="center" style="margin-top: 5px"> - <label value="&backgroundColor.label;" accesskey="&backgroundColor.accesskey;" control="backgroundmenu"/> - <spacer flex="1"/> - <colorpicker type="button" id="backgroundmenu" palettename="standard" - preference="browser.display.background_color"/> - </hbox> - <separator class="thin"/> - <hbox align="center"> - <checkbox id="browserUseSystemColors" label="&useSystemColors.label;" accesskey="&useSystemColors.accesskey;" - preference="browser.display.use_system_colors"/> - </hbox> - </groupbox> - - <groupbox flex="1"> - <caption label="&links;"/> - <hbox align="center"> - <label value="&linkColor.label;" accesskey="&linkColor.accesskey;" control="unvisitedlinkmenu"/> - <spacer flex="1"/> - <colorpicker type="button" id="unvisitedlinkmenu" palettename="standard" - preference="browser.anchor_color"/> - </hbox> - <hbox align="center" style="margin-top: 5px"> - <label value="&visitedLinkColor.label;" accesskey="&visitedLinkColor.accesskey;" control="visitedlinkmenu"/> - <spacer flex="1"/> - <colorpicker type="button" id="visitedlinkmenu" palettename="standard" - preference="browser.visited_color"/> - </hbox> - <separator class="thin"/> - <hbox align="center"> - <checkbox id="browserUnderlineAnchors" label="&underlineLinks.label;" accesskey="&underlineLinks.accesskey;" - preference="browser.underline_anchors"/> - </hbox> - </groupbox> - </hbox> -#ifdef XP_WIN - <vbox align="start"> -#else - <vbox> -#endif - <label accesskey="&overridePageColors.accesskey;" - control="useDocumentColors">&overridePageColors.label;</label> - <menulist id="useDocumentColors" preference="browser.display.document_color_use"> - <menupopup> - <menuitem label="&overridePageColors.always.label;" - value="2" id="documentColorAlways"/> - <menuitem label="&overridePageColors.auto.label;" - value="0" id="documentColorAutomatic"/> - <menuitem label="&overridePageColors.never.label;" - value="1" id="documentColorNever"/> - </menupopup> - </menulist> - </vbox> - </prefpane> -</prefwindow> diff --git a/components/preferences/connection.js b/components/preferences/connection.js deleted file mode 100644 index da038c9..0000000 --- a/components/preferences/connection.js +++ /dev/null @@ -1,200 +0,0 @@ -/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var gConnectionsDialog = { - beforeAccept: function () - { - var proxyTypePref = document.getElementById("network.proxy.type"); - if (proxyTypePref.value == 2) { - this.doAutoconfigURLFixup(); - return true; - } - - if (proxyTypePref.value != 1) - return true; - - var httpProxyURLPref = document.getElementById("network.proxy.http"); - var httpProxyPortPref = document.getElementById("network.proxy.http_port"); - var shareProxiesPref = document.getElementById("network.proxy.share_proxy_settings"); - if (shareProxiesPref.value) { - var proxyPrefs = ["ssl", "ftp", "socks"]; - for (var i = 0; i < proxyPrefs.length; ++i) { - var proxyServerURLPref = document.getElementById("network.proxy." + proxyPrefs[i]); - var proxyPortPref = document.getElementById("network.proxy." + proxyPrefs[i] + "_port"); - var backupServerURLPref = document.getElementById("network.proxy.backup." + proxyPrefs[i]); - var backupPortPref = document.getElementById("network.proxy.backup." + proxyPrefs[i] + "_port"); - backupServerURLPref.value = proxyServerURLPref.value; - backupPortPref.value = proxyPortPref.value; - proxyServerURLPref.value = httpProxyURLPref.value; - proxyPortPref.value = httpProxyPortPref.value; - } - } - - this.sanitizeNoProxiesPref(); - - return true; - }, - - checkForSystemProxy: function () - { - if ("@mozilla.org/system-proxy-settings;1" in Components.classes) - document.getElementById("systemPref").removeAttribute("hidden"); - }, - - proxyTypeChanged: function () - { - var proxyTypePref = document.getElementById("network.proxy.type"); - - // Update http - var httpProxyURLPref = document.getElementById("network.proxy.http"); - httpProxyURLPref.disabled = proxyTypePref.value != 1; - var httpProxyPortPref = document.getElementById("network.proxy.http_port"); - httpProxyPortPref.disabled = proxyTypePref.value != 1; - - // Now update the other protocols - this.updateProtocolPrefs(); - - var shareProxiesPref = document.getElementById("network.proxy.share_proxy_settings"); - shareProxiesPref.disabled = proxyTypePref.value != 1; - - var autologinProxyPref = document.getElementById("signon.autologin.proxy"); - autologinProxyPref.disabled = proxyTypePref.value == 0; - - var noProxiesPref = document.getElementById("network.proxy.no_proxies_on"); - noProxiesPref.disabled = proxyTypePref.value == 0; - - var autoconfigURLPref = document.getElementById("network.proxy.autoconfig_url"); - autoconfigURLPref.disabled = proxyTypePref.value != 2; - - this.updateReloadButton(); - }, - - updateDNSPref: function () - { - var socksVersionPref = document.getElementById("network.proxy.socks_version"); - var socksDNSPref = document.getElementById("network.proxy.socks_remote_dns"); - var proxyTypePref = document.getElementById("network.proxy.type"); - var isDefinitelySocks4 = !socksVersionPref.disabled && socksVersionPref.value == 4; - socksDNSPref.disabled = (isDefinitelySocks4 || proxyTypePref.value == 0); - return undefined; - }, - - updateReloadButton: function () - { - // Disable the "Reload PAC" button if the selected proxy type is not PAC or - // if the current value of the PAC textbox does not match the value stored - // in prefs. Likewise, disable the reload button if PAC is not configured - // in prefs. - - var typedURL = document.getElementById("networkProxyAutoconfigURL").value; - var proxyTypeCur = document.getElementById("network.proxy.type").value; - - var prefs = - Components.classes["@mozilla.org/preferences-service;1"]. - getService(Components.interfaces.nsIPrefBranch); - var pacURL = prefs.getCharPref("network.proxy.autoconfig_url"); - var proxyType = prefs.getIntPref("network.proxy.type"); - - var disableReloadPref = - document.getElementById("pref.advanced.proxies.disable_button.reload"); - disableReloadPref.disabled = - (proxyTypeCur != 2 || proxyType != 2 || typedURL != pacURL); - }, - - readProxyType: function () - { - this.proxyTypeChanged(); - return undefined; - }, - - updateProtocolPrefs: function () - { - var proxyTypePref = document.getElementById("network.proxy.type"); - var shareProxiesPref = document.getElementById("network.proxy.share_proxy_settings"); - var proxyPrefs = ["ssl", "ftp", "socks"]; - for (var i = 0; i < proxyPrefs.length; ++i) { - var proxyServerURLPref = document.getElementById("network.proxy." + proxyPrefs[i]); - var proxyPortPref = document.getElementById("network.proxy." + proxyPrefs[i] + "_port"); - - // Restore previous per-proxy custom settings, if present. - if (!shareProxiesPref.value) { - var backupServerURLPref = document.getElementById("network.proxy.backup." + proxyPrefs[i]); - var backupPortPref = document.getElementById("network.proxy.backup." + proxyPrefs[i] + "_port"); - if (backupServerURLPref.hasUserValue) { - proxyServerURLPref.value = backupServerURLPref.value; - backupServerURLPref.reset(); - } - if (backupPortPref.hasUserValue) { - proxyPortPref.value = backupPortPref.value; - backupPortPref.reset(); - } - } - - proxyServerURLPref.updateElements(); - proxyPortPref.updateElements(); - proxyServerURLPref.disabled = proxyTypePref.value != 1 || shareProxiesPref.value; - proxyPortPref.disabled = proxyServerURLPref.disabled; - } - var socksVersionPref = document.getElementById("network.proxy.socks_version"); - socksVersionPref.disabled = proxyTypePref.value != 1 || shareProxiesPref.value; - this.updateDNSPref(); - return undefined; - }, - - readProxyProtocolPref: function (aProtocol, aIsPort) - { - var shareProxiesPref = document.getElementById("network.proxy.share_proxy_settings"); - if (shareProxiesPref.value) { - var pref = document.getElementById("network.proxy.http" + (aIsPort ? "_port" : "")); - return pref.value; - } - - var backupPref = document.getElementById("network.proxy.backup." + aProtocol + (aIsPort ? "_port" : "")); - return backupPref.hasUserValue ? backupPref.value : undefined; - }, - - reloadPAC: function () - { - Components.classes["@mozilla.org/network/protocol-proxy-service;1"]. - getService().reloadPAC(); - }, - - doAutoconfigURLFixup: function () - { - var autoURL = document.getElementById("networkProxyAutoconfigURL"); - var autoURLPref = document.getElementById("network.proxy.autoconfig_url"); - var URIFixup = Components.classes["@mozilla.org/docshell/urifixup;1"] - .getService(Components.interfaces.nsIURIFixup); - try { - autoURLPref.value = autoURL.value = URIFixup.createFixupURI(autoURL.value, 0).spec; - } catch(ex) {} - }, - - sanitizeNoProxiesPref: function() - { - var noProxiesPref = document.getElementById("network.proxy.no_proxies_on"); - // replace substrings of ; and \n with commas if they're neither immediately - // preceded nor followed by a valid separator character - noProxiesPref.value = noProxiesPref.value.replace(/([^, \n;])[;\n]+(?![,\n;])/g, '$1,'); - // replace any remaining ; and \n since some may follow commas, etc. - noProxiesPref.value = noProxiesPref.value.replace(/[;\n]/g, ''); - }, - - readHTTPProxyServer: function () - { - var shareProxiesPref = document.getElementById("network.proxy.share_proxy_settings"); - if (shareProxiesPref.value) - this.updateProtocolPrefs(); - return undefined; - }, - - readHTTPProxyPort: function () - { - var shareProxiesPref = document.getElementById("network.proxy.share_proxy_settings"); - if (shareProxiesPref.value) - this.updateProtocolPrefs(); - return undefined; - } -}; diff --git a/components/preferences/connection.xul b/components/preferences/connection.xul deleted file mode 100644 index e6079dd..0000000 --- a/components/preferences/connection.xul +++ /dev/null @@ -1,164 +0,0 @@ -<?xml version="1.0"?> - -# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -<!DOCTYPE prefwindow SYSTEM "chrome://browser/locale/preferences/connection.dtd"> - -<?xml-stylesheet href="chrome://global/skin/"?> - -<prefwindow id="ConnectionsDialog" type="child" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - title="&connectionsDialog.title;" - dlgbuttons="accept,cancel,help" - onbeforeaccept="return gConnectionsDialog.beforeAccept();" - onload="gConnectionsDialog.checkForSystemProxy();" - ondialoghelp="openPrefsHelp()" -#ifdef XP_MACOSX - style="width: &window.macWidth; !important;"> -#else - style="width: &window.width; !important;"> -#endif - - <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/> - - <prefpane id="ConnectionsDialogPane" - helpTopic="prefs-connection-settings"> - - <preferences> - <preference id="network.proxy.type" name="network.proxy.type" type="int" - onchange="gConnectionsDialog.proxyTypeChanged();"/> - <preference id="network.proxy.http" name="network.proxy.http" type="string"/> - <preference id="network.proxy.http_port" name="network.proxy.http_port" type="int"/> - <preference id="network.proxy.ftp" name="network.proxy.ftp" type="string"/> - <preference id="network.proxy.ftp_port" name="network.proxy.ftp_port" type="int"/> - <preference id="network.proxy.ssl" name="network.proxy.ssl" type="string"/> - <preference id="network.proxy.ssl_port" name="network.proxy.ssl_port" type="int"/> - <preference id="network.proxy.socks" name="network.proxy.socks" type="string"/> - <preference id="network.proxy.socks_port" name="network.proxy.socks_port" type="int"/> - <preference id="network.proxy.socks_version" name="network.proxy.socks_version" type="int" - onchange="gConnectionsDialog.updateDNSPref();"/> - <preference id="network.proxy.socks_remote_dns" name="network.proxy.socks_remote_dns" type="bool"/> - <preference id="network.proxy.no_proxies_on" name="network.proxy.no_proxies_on" type="string"/> - <preference id="network.proxy.autoconfig_url" name="network.proxy.autoconfig_url" type="string"/> - <preference id="network.proxy.share_proxy_settings" name="network.proxy.share_proxy_settings" type="bool"/> - <preference id="signon.autologin.proxy" name="signon.autologin.proxy" type="bool"/> - <preference id="pref.advanced.proxies.disable_button.reload" - name="pref.advanced.proxies.disable_button.reload" type="bool"/> - <preference id="network.proxy.backup.ftp" name="network.proxy.backup.ftp" type="string"/> - <preference id="network.proxy.backup.ftp_port" name="network.proxy.backup.ftp_port" type="int"/> - <preference id="network.proxy.backup.ssl" name="network.proxy.backup.ssl" type="string"/> - <preference id="network.proxy.backup.ssl_port" name="network.proxy.backup.ssl_port" type="int"/> - <preference id="network.proxy.backup.socks" name="network.proxy.backup.socks" type="string"/> - <preference id="network.proxy.backup.socks_port" name="network.proxy.backup.socks_port" type="int"/> - </preferences> - - <script type="application/javascript" src="chrome://browser/content/preferences/connection.js"/> - - <stringbundle id="preferencesBundle" src="chrome://browser/locale/preferences/preferences.properties"/> - - <groupbox> - <caption label="&proxyTitle.label;"/> - - <radiogroup id="networkProxyType" preference="network.proxy.type" - onsyncfrompreference="return gConnectionsDialog.readProxyType();"> - <radio value="0" label="&noProxyTypeRadio.label;" accesskey="&noProxyTypeRadio.accesskey;"/> - <radio value="4" label="&WPADTypeRadio.label;" accesskey="&WPADTypeRadio.accesskey;"/> - <radio value="5" label="&systemTypeRadio.label;" accesskey="&systemTypeRadio.accesskey;" id="systemPref" hidden="true"/> - <radio value="1" label="&manualTypeRadio.label;" accesskey="&manualTypeRadio.accesskey;"/> - <grid class="indent" flex="1"> - <columns> - <column/> - <column flex="1"/> - </columns> - <rows> - <row align="center"> - <hbox pack="end"> - <label value="&http.label;" accesskey="&http.accesskey;" control="networkProxyHTTP"/> - </hbox> - <hbox align="center"> - <textbox id="networkProxyHTTP" flex="1" - preference="network.proxy.http" onsyncfrompreference="return gConnectionsDialog.readHTTPProxyServer();"/> - <label value="&port.label;" accesskey="&HTTPport.accesskey;" control="networkProxyHTTP_Port"/> - <textbox id="networkProxyHTTP_Port" type="number" max="65535" size="5" - preference="network.proxy.http_port" onsyncfrompreference="return gConnectionsDialog.readHTTPProxyPort();"/> - </hbox> - </row> - <row> - <hbox/> - <hbox> - <checkbox id="shareAllProxies" label="&shareproxy.label;" accesskey="&shareproxy.accesskey;" - preference="network.proxy.share_proxy_settings" - onsyncfrompreference="return gConnectionsDialog.updateProtocolPrefs();"/> - </hbox> - </row> - <row align="center"> - <hbox pack="end"> - <label value="&ssl.label;" accesskey="&ssl.accesskey;" control="networkProxySSL"/> - </hbox> - <hbox align="center"> - <textbox id="networkProxySSL" flex="1" preference="network.proxy.ssl" - onsyncfrompreference="return gConnectionsDialog.readProxyProtocolPref('ssl', false);"/> - <label value="&port.label;" accesskey="&SSLport.accesskey;" control="networkProxySSL_Port"/> - <textbox id="networkProxySSL_Port" type="number" max="65535" size="5" preference="network.proxy.ssl_port" - onsyncfrompreference="return gConnectionsDialog.readProxyProtocolPref('ssl', true);"/> - </hbox> - </row> - <row align="center"> - <hbox pack="end"> - <label value="&ftp.label;" accesskey="&ftp.accesskey;" control="networkProxyFTP"/> - </hbox> - <hbox align="center"> - <textbox id="networkProxyFTP" flex="1" preference="network.proxy.ftp" - onsyncfrompreference="return gConnectionsDialog.readProxyProtocolPref('ftp', false);"/> - <label value="&port.label;" accesskey="&FTPport.accesskey;" control="networkProxyFTP_Port"/> - <textbox id="networkProxyFTP_Port" type="number" max="65535" size="5" preference="network.proxy.ftp_port" - onsyncfrompreference="return gConnectionsDialog.readProxyProtocolPref('ftp', true);"/> - </hbox> - </row> - <row align="center"> - <hbox pack="end"> - <label value="&socks.label;" accesskey="&socks.accesskey;" control="networkProxySOCKS"/> - </hbox> - <hbox align="center"> - <textbox id="networkProxySOCKS" flex="1" preference="network.proxy.socks" - onsyncfrompreference="return gConnectionsDialog.readProxyProtocolPref('socks', false);"/> - <label value="&port.label;" accesskey="&SOCKSport.accesskey;" control="networkProxySOCKS_Port"/> - <textbox id="networkProxySOCKS_Port" type="number" max="65535" size="5" preference="network.proxy.socks_port" - onsyncfrompreference="return gConnectionsDialog.readProxyProtocolPref('socks', true);"/> - </hbox> - </row> - <row> - <spacer/> - <radiogroup id="networkProxySOCKSVersion" orient="horizontal" - preference="network.proxy.socks_version"> - <radio id="networkProxySOCKSVersion4" value="4" label="&socks4.label;" accesskey="&socks4.accesskey;"/> - <radio id="networkProxySOCKSVersion5" value="5" label="&socks5.label;" accesskey="&socks5.accesskey;"/> - </radiogroup> - </row> - </rows> - </grid> - <radio value="2" label="&autoTypeRadio.label;" accesskey="&autoTypeRadio.accesskey;"/> - <hbox class="indent" flex="1" align="center"> - <textbox id="networkProxyAutoconfigURL" flex="1" preference="network.proxy.autoconfig_url" - oninput="gConnectionsDialog.updateReloadButton();"/> - <button id="autoReload" icon="refresh" - label="&reload.label;" accesskey="&reload.accesskey;" - oncommand="gConnectionsDialog.reloadPAC();" - preference="pref.advanced.proxies.disable_button.reload"/> - </hbox> - </radiogroup> - <separator class="thin"/> - <label value="&noproxy.label;" accesskey="&noproxy.accesskey;" control="networkProxyNone"/> - <textbox id="networkProxyNone" preference="network.proxy.no_proxies_on" multiline="true" rows="2"/> - <label value="&noproxyExplain.label;" control="networkProxyNone"/> - <checkbox id="autologinProxy" preference="signon.autologin.proxy" - label="&autologinproxy.label;" accesskey="&autologinproxy.accesskey;" - tooltiptext="&autologinproxy.tooltip;"/> - <checkbox id="networkProxySOCKSRemoteDNS" preference="network.proxy.socks_remote_dns" - label="&socksRemoteDNS.label;" accesskey="&socksRemoteDNS.accesskey;"/> - </groupbox> - </prefpane> -</prefwindow> diff --git a/components/preferences/content.js b/components/preferences/content.js deleted file mode 100644 index 5ae84c2..0000000 --- a/components/preferences/content.js +++ /dev/null @@ -1,187 +0,0 @@ -/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var gContentPane = { - - /** - * Initializes the fonts dropdowns displayed in this pane. - */ - init: function () - { - this._rebuildFonts(); - var menulist = document.getElementById("defaultFont"); - if (menulist.selectedIndex == -1) { - menulist.insertItemAt(0, "", "", ""); - menulist.selectedIndex = 0; - } - }, - - // UTILITY FUNCTIONS - - /** - * Utility function to enable/disable the button specified by aButtonID based - * on the value of the Boolean preference specified by aPreferenceID. - */ - updateButtons: function (aButtonID, aPreferenceID) - { - var button = document.getElementById(aButtonID); - var preference = document.getElementById(aPreferenceID); - button.disabled = preference.value != true; - return undefined; - }, - - /** - * Utility function to enable/disable the checkboxes for MSE options depending - * on the value of media.mediasource.enabled. - */ - updateMSE: function () - { - var checkboxMSEMP4 = document.getElementById('videoMSEMP4'); - var checkboxMSEWebM = document.getElementById('videoMSEWebM'); - var preference = document.getElementById('media.mediasource.enabled'); - checkboxMSEMP4.disabled = preference.value != true; - checkboxMSEWebM.disabled = preference.value != true; - }, - - // BEGIN UI CODE - - /* - * Preferences: - * - * dom.disable_open_during_load - * - true if popups are blocked by default, false otherwise - */ - - // POP-UPS - - /** - * Displays the popup exceptions dialog where specific site popup preferences - * can be set. - */ - showPopupExceptions: function () - { - var bundlePreferences = document.getElementById("bundlePreferences"); - var params = { blockVisible: false, sessionVisible: false, allowVisible: true, prefilledHost: "", permissionType: "popup" }; - params.windowTitle = bundlePreferences.getString("popuppermissionstitle"); - params.introText = bundlePreferences.getString("popuppermissionstext"); - document.documentElement.openWindow("Browser:Permissions", - "chrome://browser/content/preferences/permissions.xul", - "", params); - }, - - - // FONTS - - /** - * Populates the default font list in UI. - */ - _rebuildFonts: function () - { - var langGroupPref = document.getElementById("font.language.group"); - this._selectDefaultLanguageGroup(langGroupPref.value, - this._readDefaultFontTypeForLanguage(langGroupPref.value) == "serif"); - }, - - /** - * - */ - _selectDefaultLanguageGroup: function (aLanguageGroup, aIsSerif) - { - const kFontNameFmtSerif = "font.name.serif.%LANG%"; - const kFontNameFmtSansSerif = "font.name.sans-serif.%LANG%"; - const kFontNameListFmtSerif = "font.name-list.serif.%LANG%"; - const kFontNameListFmtSansSerif = "font.name-list.sans-serif.%LANG%"; - const kFontSizeFmtVariable = "font.size.variable.%LANG%"; - - var prefs = [{ format : aIsSerif ? kFontNameFmtSerif : kFontNameFmtSansSerif, - type : "fontname", - element : "defaultFont", - fonttype : aIsSerif ? "serif" : "sans-serif" }, - { format : aIsSerif ? kFontNameListFmtSerif : kFontNameListFmtSansSerif, - type : "unichar", - element : null, - fonttype : aIsSerif ? "serif" : "sans-serif" }, - { format : kFontSizeFmtVariable, - type : "int", - element : "defaultFontSize", - fonttype : null }]; - var preferences = document.getElementById("contentPreferences"); - for (var i = 0; i < prefs.length; ++i) { - var preference = document.getElementById(prefs[i].format.replace(/%LANG%/, aLanguageGroup)); - if (!preference) { - preference = document.createElement("preference"); - var name = prefs[i].format.replace(/%LANG%/, aLanguageGroup); - preference.id = name; - preference.setAttribute("name", name); - preference.setAttribute("type", prefs[i].type); - preferences.appendChild(preference); - } - - if (!prefs[i].element) - continue; - - var element = document.getElementById(prefs[i].element); - if (element) { - element.setAttribute("preference", preference.id); - - if (prefs[i].fonttype) - FontBuilder.buildFontList(aLanguageGroup, prefs[i].fonttype, element); - - preference.setElementValue(element); - } - } - }, - - /** - * Returns the type of the current default font for the language denoted by - * aLanguageGroup. - */ - _readDefaultFontTypeForLanguage: function (aLanguageGroup) - { - const kDefaultFontType = "font.default.%LANG%"; - var defaultFontTypePref = kDefaultFontType.replace(/%LANG%/, aLanguageGroup); - var preference = document.getElementById(defaultFontTypePref); - if (!preference) { - preference = document.createElement("preference"); - preference.id = defaultFontTypePref; - preference.setAttribute("name", defaultFontTypePref); - preference.setAttribute("type", "string"); - preference.setAttribute("onchange", "gContentPane._rebuildFonts();"); - document.getElementById("contentPreferences").appendChild(preference); - } - return preference.value; - }, - - /** - * Displays the fonts dialog, where web page font names and sizes can be - * configured. - */ - configureFonts: function () - { - document.documentElement.openSubDialog("chrome://browser/content/preferences/fonts.xul", - "", null); - }, - - /** - * Displays the colors dialog, where default web page/link/etc. colors can be - * configured. - */ - configureColors: function () - { - document.documentElement.openSubDialog("chrome://browser/content/preferences/colors.xul", - "", null); - }, - - // LANGUAGES - - /** - * Shows a dialog in which the preferred language for web content may be set. - */ - showLanguages: function () - { - document.documentElement.openSubDialog("chrome://browser/content/preferences/languages.xul", - "", null); - } -}; diff --git a/components/preferences/content.xul b/components/preferences/content.xul deleted file mode 100644 index 23d942e..0000000 --- a/components/preferences/content.xul +++ /dev/null @@ -1,171 +0,0 @@ -<?xml version="1.0"?> - -<!-- -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- --> -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<!DOCTYPE overlay [ - <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> - <!ENTITY % contentDTD SYSTEM "chrome://browser/locale/preferences/content.dtd"> - %brandDTD; - %contentDTD; -]> - -<overlay id="ContentPaneOverlay" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - - <prefpane id="paneContent" - onpaneload="gContentPane.init();" - helpTopic="prefs-content"> - - <preferences id="contentPreferences"> - <!--XXX buttons prefs --> - - <!-- POPUPS, IMAGES --> - <preference id="dom.disable_open_during_load" name="dom.disable_open_during_load" type="bool"/> - <preference id="permissions.default.image" name="permissions.default.image" type="int"/> - - <!-- FONTS --> - <preference id="font.language.group" - name="font.language.group" - type="wstring" - onchange="gContentPane._rebuildFonts();"/> - - <!-- VIDEO --> - <preference id="media.mediasource.enabled" name="media.mediasource.enabled" type="bool"/> - <preference id="media.mediasource.mp4.enabled" name="media.mediasource.mp4.enabled" type="bool"/> - <preference id="media.mediasource.webm.enabled" name="media.mediasource.webm.enabled" type="bool"/> - - </preferences> - - <script type="application/javascript" src="chrome://mozapps/content/preferences/fontbuilder.js"/> - <script type="application/javascript" src="chrome://browser/content/preferences/content.js"/> - - <stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/> - - <!-- various checkboxes, font-fu --> - <groupbox id="miscGroup"> - <grid id="contentGrid"> - <columns> - <column flex="1"/> - <column/> - </columns> - <rows id="contentRows-1"> - <row id="popupPolicyRow"> - <vbox align="start"> - <checkbox id="popupPolicy" preference="dom.disable_open_during_load" - label="&blockPopups.label;" accesskey="&blockPopups.accesskey;" - onsyncfrompreference="return gContentPane.updateButtons('popupPolicyButton', - 'dom.disable_open_during_load');"/> - </vbox> - <button id="popupPolicyButton" label="&popupExceptions.label;" - oncommand="gContentPane.showPopupExceptions();" - accesskey="&popupExceptions.accesskey;"/> - </row> - <row id="enableImagesRow"> - <hbox align="center"> - <label id="loadImages" control="loadImages-menu">&loadImages.label;</label> - <menulist id="loadImages-menu" preference="permissions.default.image" sizetopopup="always"> - <menupopup> - <menuitem label="&loadImages.always;" value="1" /> - <menuitem label="&loadImages.never;" value="2" /> - <menuitem label="&loadImages.no3rdparty;" value="3" /> - </menupopup> - </menulist> - </hbox> - </row> - </rows> - </grid> - </groupbox> - - <!-- Fonts and Colors --> - <groupbox id="fontsGroup"> - <caption label="&fontsAndColors.label;"/> - - <grid id="fontsGrid"> - <columns> - <column flex="1"/> - <column/> - </columns> - <rows id="fontsRows"> - <row id="fontRow"> - <hbox align="center"> - <label control="defaultFont" accesskey="&defaultFont.accesskey;">&defaultFont.label;</label> - <menulist id="defaultFont" flex="1"/> - <label control="defaultFontSize" accesskey="&defaultSize.accesskey;">&defaultSize.label;</label> - <menulist id="defaultFontSize"> - <menupopup> - <menuitem value="9" label="9"/> - <menuitem value="10" label="10"/> - <menuitem value="11" label="11"/> - <menuitem value="12" label="12"/> - <menuitem value="13" label="13"/> - <menuitem value="14" label="14"/> - <menuitem value="15" label="15"/> - <menuitem value="16" label="16"/> - <menuitem value="17" label="17"/> - <menuitem value="18" label="18"/> - <menuitem value="20" label="20"/> - <menuitem value="22" label="22"/> - <menuitem value="24" label="24"/> - <menuitem value="26" label="26"/> - <menuitem value="28" label="28"/> - <menuitem value="30" label="30"/> - <menuitem value="32" label="32"/> - <menuitem value="34" label="34"/> - <menuitem value="36" label="36"/> - <menuitem value="40" label="40"/> - <menuitem value="44" label="44"/> - <menuitem value="48" label="48"/> - <menuitem value="56" label="56"/> - <menuitem value="64" label="64"/> - <menuitem value="72" label="72"/> - </menupopup> - </menulist> - </hbox> - <button id="advancedFonts" icon="select-font" - label="&advancedFonts.label;" - accesskey="&advancedFonts.accesskey;" - oncommand="gContentPane.configureFonts();"/> - </row> - <row id="colorsRow"> - <hbox/> - <button id="colors" icon="select-color" - label="&colors.label;" - accesskey="&colors.accesskey;" - oncommand="gContentPane.configureColors();"/> - </row> - </rows> - </grid> - </groupbox> - - <!-- Languages --> - <groupbox id="languagesGroup"> - <caption label="&languages.label;"/> - - <hbox id="languagesBox" align="center"> - <description flex="1" control="chooseLanguage">&chooseLanguage.label;</description> - <button id="chooseLanguage" - label="&chooseButton.label;" - accesskey="&chooseButton.accesskey;" - oncommand="gContentPane.showLanguages();"/> - </hbox> - </groupbox> - - <!-- Video --> - <groupbox id="videoGroup"> - <caption label="&video.label;"/> - - <checkbox id="videoMSE" preference="media.mediasource.enabled" - label="&videoMSE.label;" accesskey="&videoMSE.accesskey;" - onsyncfrompreference="gContentPane.updateMSE();"/> - <checkbox class="indent" id="videoMSEMP4" preference="media.mediasource.mp4.enabled" - label="&videoMSEMP4.label;" accesskey="&videoMSEMP4.accesskey;"/> - <checkbox class="indent" id="videoMSEWebM" preference="media.mediasource.webm.enabled" - label="&videoMSEWebM.label;" accesskey="&videoMSEWebM.accesskey;"/> - </groupbox> - - </prefpane> - -</overlay> diff --git a/components/preferences/cookies.js b/components/preferences/cookies.js deleted file mode 100644 index 4fa47ee..0000000 --- a/components/preferences/cookies.js +++ /dev/null @@ -1,948 +0,0 @@ -/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -const nsICookie = Components.interfaces.nsICookie; - -Components.utils.import("resource://gre/modules/PluralForm.jsm"); - -var gCookiesWindow = { - _cm : Components.classes["@mozilla.org/cookiemanager;1"] - .getService(Components.interfaces.nsICookieManager), - _ds : Components.classes["@mozilla.org/intl/scriptabledateformat;1"] - .getService(Components.interfaces.nsIScriptableDateFormat), - _hosts : {}, - _hostOrder : [], - _tree : null, - _bundle : null, - - init: function () { - var os = Components.classes["@mozilla.org/observer-service;1"] - .getService(Components.interfaces.nsIObserverService); - os.addObserver(this, "cookie-changed", false); - os.addObserver(this, "perm-changed", false); - - this._bundle = document.getElementById("bundlePreferences"); - this._tree = document.getElementById("cookiesList"); - - let removeAllCookies = document.getElementById("removeAllCookies"); - removeAllCookies.setAttribute("accesskey", this._bundle.getString("removeAllCookies.accesskey")); - let removeSelectedCookies = document.getElementById("removeSelectedCookies"); - removeSelectedCookies.setAttribute("accesskey", this._bundle.getString("removeSelectedCookies.accesskey")); - - this._populateList(true); - - document.getElementById("filter").focus(); - }, - - uninit: function () { - var os = Components.classes["@mozilla.org/observer-service;1"] - .getService(Components.interfaces.nsIObserverService); - os.removeObserver(this, "cookie-changed"); - os.removeObserver(this, "perm-changed"); - }, - - _populateList: function (aInitialLoad) { - this._loadCookies(); - this._tree.view = this._view; - if (aInitialLoad) - this.sort("rawHost"); - if (this._view.rowCount > 0) - this._tree.view.selection.select(0); - - if (aInitialLoad) { - if ("arguments" in window && - window.arguments[0] && - window.arguments[0].filterString) - this.setFilter(window.arguments[0].filterString); - } - else { - if (document.getElementById("filter").value != "") - this.filter(); - } - - this._updateRemoveAllButton(); - - this._saveState(); - }, - - _cookieEquals: function (aCookieA, aCookieB, aStrippedHost) { - return aCookieA.rawHost == aStrippedHost && - aCookieA.name == aCookieB.name && - aCookieA.path == aCookieB.path && - ChromeUtils.isOriginAttributesEqual(aCookieA.originAttributes, - aCookieB.originAttributes); - }, - - observe: function (aCookie, aTopic, aData) { - if (aTopic != "cookie-changed") - return; - - if (aCookie instanceof Components.interfaces.nsICookie) { - var strippedHost = this._makeStrippedHost(aCookie.host); - if (aData == "changed") - this._handleCookieChanged(aCookie, strippedHost); - else if (aData == "added") - this._handleCookieAdded(aCookie, strippedHost); - } - else if (aData == "cleared") { - this._hosts = {}; - this._hostOrder = []; - - var oldRowCount = this._view._rowCount; - this._view._rowCount = 0; - this._tree.treeBoxObject.rowCountChanged(0, -oldRowCount); - this._view.selection.clearSelection(); - this._updateRemoveAllButton(); - } - else if (aData == "reload") { - // first, clear any existing entries - this.observe(aCookie, aTopic, "cleared"); - - // then, reload the list - this._populateList(false); - } - - // We don't yet handle aData == "deleted" - it's a less common case - // and is rather complicated as selection tracking is difficult - }, - - _handleCookieChanged: function (changedCookie, strippedHost) { - var rowIndex = 0; - var cookieItem = null; - if (!this._view._filtered) { - for (var i = 0; i < this._hostOrder.length; ++i) { // (var host in this._hosts) { - ++rowIndex; - var hostItem = this._hosts[this._hostOrder[i]]; // var hostItem = this._hosts[host]; - if (this._hostOrder[i] == strippedHost) { // host == strippedHost) { - // Host matches, look for the cookie within this Host collection - // and update its data - for (var j = 0; j < hostItem.cookies.length; ++j) { - ++rowIndex; - var currCookie = hostItem.cookies[j]; - if (this._cookieEquals(currCookie, changedCookie, strippedHost)) { - currCookie.value = changedCookie.value; - currCookie.isSecure = changedCookie.isSecure; - currCookie.isDomain = changedCookie.isDomain; - currCookie.expires = changedCookie.expires; - cookieItem = currCookie; - break; - } - } - } - else if (hostItem.open) - rowIndex += hostItem.cookies.length; - } - } - else { - // Just walk the filter list to find the item. It doesn't matter that - // we don't update the main Host collection when we do this, because - // when the filter is reset the Host collection is rebuilt anyway. - for (rowIndex = 0; rowIndex < this._view._filterSet.length; ++rowIndex) { - currCookie = this._view._filterSet[rowIndex]; - if (this._cookieEquals(currCookie, changedCookie, strippedHost)) { - currCookie.value = changedCookie.value; - currCookie.isSecure = changedCookie.isSecure; - currCookie.isDomain = changedCookie.isDomain; - currCookie.expires = changedCookie.expires; - cookieItem = currCookie; - break; - } - } - } - - // Make sure the tree display is up to date... - this._tree.treeBoxObject.invalidateRow(rowIndex); - // ... and if the cookie is selected, update the displayed metadata too - if (cookieItem != null && this._view.selection.currentIndex == rowIndex) - this._updateCookieData(cookieItem); - }, - - _handleCookieAdded: function (changedCookie, strippedHost) { - var rowCountImpact = 0; - var addedHost = { value: 0 }; - this._addCookie(strippedHost, changedCookie, addedHost); - if (!this._view._filtered) { - // The Host collection for this cookie already exists, and it's not open, - // so don't increment the rowCountImpact becaues the user is not going to - // see the additional rows as they're hidden. - if (addedHost.value || this._hosts[strippedHost].open) - ++rowCountImpact; - } - else { - // We're in search mode, and the cookie being added matches - // the search condition, so add it to the list. - var c = this._makeCookieObject(strippedHost, changedCookie); - if (this._cookieMatchesFilter(c)) { - this._view._filterSet.push(this._makeCookieObject(strippedHost, changedCookie)); - ++rowCountImpact; - } - } - // Now update the tree display at the end (we could/should re run the sort - // if any to get the position correct.) - var oldRowCount = this._rowCount; - this._view._rowCount += rowCountImpact; - this._tree.treeBoxObject.rowCountChanged(oldRowCount - 1, rowCountImpact); - - this._updateRemoveAllButton(); - }, - - _view: { - _filtered : false, - _filterSet : [], - _filterValue: "", - _rowCount : 0, - _cacheValid : 0, - _cacheItems : [], - get rowCount() { - return this._rowCount; - }, - - _getItemAtIndex: function (aIndex) { - if (this._filtered) - return this._filterSet[aIndex]; - - var start = 0; - var count = 0, hostIndex = 0; - - var cacheIndex = Math.min(this._cacheValid, aIndex); - if (cacheIndex > 0) { - var cacheItem = this._cacheItems[cacheIndex]; - start = cacheItem['start']; - count = hostIndex = cacheItem['count']; - } - - for (var i = start; i < gCookiesWindow._hostOrder.length; ++i) { // var host in gCookiesWindow._hosts) { - var currHost = gCookiesWindow._hosts[gCookiesWindow._hostOrder[i]]; // gCookiesWindow._hosts[host]; - if (!currHost) continue; - if (count == aIndex) - return currHost; - hostIndex = count; - - var cacheEntry = { 'start' : i, 'count' : count }; - var cacheStart = count; - - if (currHost.open) { - if (count < aIndex && aIndex <= (count + currHost.cookies.length)) { - // We are looking for an entry within this host's children, - // enumerate them looking for the index. - ++count; - for (var i = 0; i < currHost.cookies.length; ++i) { - if (count == aIndex) { - var cookie = currHost.cookies[i]; - cookie.parentIndex = hostIndex; - return cookie; - } - ++count; - } - } - else { - // A host entry was open, but we weren't looking for an index - // within that host entry's children, so skip forward over the - // entry's children. We need to add one to increment for the - // host value too. - count += currHost.cookies.length + 1; - } - } - else - ++count; - - for (var j = cacheStart; j < count; j++) - this._cacheItems[j] = cacheEntry; - this._cacheValid = count - 1; - } - return null; - }, - - _removeItemAtIndex: function (aIndex, aCount) { - var removeCount = aCount === undefined ? 1 : aCount; - if (this._filtered) { - // remove the cookies from the unfiltered set so that they - // don't reappear when the filter is changed. See bug 410863. - for (var i = aIndex; i < aIndex + removeCount; ++i) { - var item = this._filterSet[i]; - var parent = gCookiesWindow._hosts[item.rawHost]; - for (var j = 0; j < parent.cookies.length; ++j) { - if (item == parent.cookies[j]) { - parent.cookies.splice(j, 1); - break; - } - } - } - this._filterSet.splice(aIndex, removeCount); - return; - } - - var item = this._getItemAtIndex(aIndex); - if (!item) return; - this._invalidateCache(aIndex - 1); - if (item.container) { - gCookiesWindow._hosts[item.rawHost] = null; - } else { - var parent = this._getItemAtIndex(item.parentIndex); - for (var i = 0; i < parent.cookies.length; ++i) { - var cookie = parent.cookies[i]; - if (item.rawHost == cookie.rawHost && - item.name == cookie.name && - item.path == cookie.path && - ChromeUtils.isOriginAttributesEqual(item.originAttributes, - cookie.originAttributes)) { - parent.cookies.splice(i, removeCount); - } - } - } - }, - - _invalidateCache: function (aIndex) { - this._cacheValid = Math.min(this._cacheValid, aIndex); - }, - - getCellText: function (aIndex, aColumn) { - if (!this._filtered) { - var item = this._getItemAtIndex(aIndex); - if (!item) - return ""; - if (aColumn.id == "domainCol") - return item.rawHost; - else if (aColumn.id == "nameCol") - return item.name; - } - else { - if (aColumn.id == "domainCol") - return this._filterSet[aIndex].rawHost; - else if (aColumn.id == "nameCol") - return this._filterSet[aIndex].name; - } - return ""; - }, - - _selection: null, - get selection () { return this._selection; }, - set selection (val) { this._selection = val; return val; }, - getRowProperties: function (aIndex) { return ""; }, - getCellProperties: function (aIndex, aColumn) { return ""; }, - getColumnProperties: function (aColumn) { return ""; }, - isContainer: function (aIndex) { - if (!this._filtered) { - var item = this._getItemAtIndex(aIndex); - if (!item) return false; - return item.container; - } - return false; - }, - isContainerOpen: function (aIndex) { - if (!this._filtered) { - var item = this._getItemAtIndex(aIndex); - if (!item) return false; - return item.open; - } - return false; - }, - isContainerEmpty: function (aIndex) { - if (!this._filtered) { - var item = this._getItemAtIndex(aIndex); - if (!item) return false; - return item.cookies.length == 0; - } - return false; - }, - isSeparator: function (aIndex) { return false; }, - isSorted: function (aIndex) { return false; }, - canDrop: function (aIndex, aOrientation) { return false; }, - drop: function (aIndex, aOrientation) {}, - getParentIndex: function (aIndex) { - if (!this._filtered) { - var item = this._getItemAtIndex(aIndex); - // If an item has no parent index (i.e. it is at the top level) this - // function MUST return -1 otherwise we will go into an infinite loop. - // Containers are always top level items in the cookies tree, so make - // sure to return the appropriate value here. - if (!item || item.container) return -1; - return item.parentIndex; - } - return -1; - }, - hasNextSibling: function (aParentIndex, aIndex) { - if (!this._filtered) { - // |aParentIndex| appears to be bogus, but we can get the real - // parent index by getting the entry for |aIndex| and reading the - // parentIndex field. - // The index of the last item in this host collection is the - // index of the parent + the size of the host collection, and - // aIndex has a next sibling if it is less than this value. - var item = this._getItemAtIndex(aIndex); - if (item) { - if (item.container) { - for (var i = aIndex + 1; i < this.rowCount; ++i) { - var subsequent = this._getItemAtIndex(i); - if (subsequent.container) - return true; - } - return false; - } - else { - var parent = this._getItemAtIndex(item.parentIndex); - if (parent && parent.container) - return aIndex < item.parentIndex + parent.cookies.length; - } - } - } - return aIndex < this.rowCount - 1; - }, - hasPreviousSibling: function (aIndex) { - if (!this._filtered) { - var item = this._getItemAtIndex(aIndex); - if (!item) return false; - var parent = this._getItemAtIndex(item.parentIndex); - if (parent && parent.container) - return aIndex > item.parentIndex + 1; - } - return aIndex > 0; - }, - getLevel: function (aIndex) { - if (!this._filtered) { - var item = this._getItemAtIndex(aIndex); - if (!item) return 0; - return item.level; - } - return 0; - }, - getImageSrc: function (aIndex, aColumn) {}, - getProgressMode: function (aIndex, aColumn) {}, - getCellValue: function (aIndex, aColumn) {}, - setTree: function (aTree) {}, - toggleOpenState: function (aIndex) { - if (!this._filtered) { - var item = this._getItemAtIndex(aIndex); - if (!item) return; - this._invalidateCache(aIndex); - var multiplier = item.open ? -1 : 1; - var delta = multiplier * item.cookies.length; - this._rowCount += delta; - item.open = !item.open; - gCookiesWindow._tree.treeBoxObject.rowCountChanged(aIndex + 1, delta); - gCookiesWindow._tree.treeBoxObject.invalidateRow(aIndex); - } - }, - cycleHeader: function (aColumn) {}, - selectionChanged: function () {}, - cycleCell: function (aIndex, aColumn) {}, - isEditable: function (aIndex, aColumn) { - return false; - }, - isSelectable: function (aIndex, aColumn) { - return false; - }, - setCellValue: function (aIndex, aColumn, aValue) {}, - setCellText: function (aIndex, aColumn, aValue) {}, - performAction: function (aAction) {}, - performActionOnRow: function (aAction, aIndex) {}, - performActionOnCell: function (aAction, aindex, aColumn) {} - }, - - _makeStrippedHost: function (aHost) { - var formattedHost = aHost.charAt(0) == "." ? aHost.substring(1, aHost.length) : aHost; - return formattedHost.substring(0, 4) == "www." ? formattedHost.substring(4, formattedHost.length) : formattedHost; - }, - - _addCookie: function (aStrippedHost, aCookie, aHostCount) { - if (!(aStrippedHost in this._hosts) || !this._hosts[aStrippedHost]) { - this._hosts[aStrippedHost] = { cookies : [], - rawHost : aStrippedHost, - level : 0, - open : false, - container : true }; - this._hostOrder.push(aStrippedHost); - ++aHostCount.value; - } - - var c = this._makeCookieObject(aStrippedHost, aCookie); - this._hosts[aStrippedHost].cookies.push(c); - }, - - _makeCookieObject: function (aStrippedHost, aCookie) { - var host = aCookie.host; - var formattedHost = host.charAt(0) == "." ? host.substring(1, host.length) : host; - var c = { name : aCookie.name, - value : aCookie.value, - isDomain : aCookie.isDomain, - host : aCookie.host, - rawHost : aStrippedHost, - path : aCookie.path, - isSecure : aCookie.isSecure, - expires : aCookie.expires, - level : 1, - container : false, - originAttributes: aCookie.originAttributes }; - return c; - }, - - _loadCookies: function () { - var e = this._cm.enumerator; - var hostCount = { value: 0 }; - this._hosts = {}; - this._hostOrder = []; - while (e.hasMoreElements()) { - var cookie = e.getNext(); - if (cookie && cookie instanceof Components.interfaces.nsICookie) { - var strippedHost = this._makeStrippedHost(cookie.host); - this._addCookie(strippedHost, cookie, hostCount); - } - else - break; - } - this._view._rowCount = hostCount.value; - }, - - formatExpiresString: function (aExpires) { - if (aExpires) { - var date = new Date(1000 * aExpires); - return this._ds.FormatDateTime("", this._ds.dateFormatLong, - this._ds.timeFormatSeconds, - date.getFullYear(), - date.getMonth() + 1, - date.getDate(), - date.getHours(), - date.getMinutes(), - date.getSeconds()); - } - return this._bundle.getString("expireAtEndOfSession"); - }, - - _updateCookieData: function (aItem) { - var seln = this._view.selection; - var ids = ["name", "value", "host", "path", "isSecure", "expires"]; - var properties; - - if (aItem && !aItem.container && seln.count > 0) { - properties = { name: aItem.name, value: aItem.value, host: aItem.host, - path: aItem.path, expires: this.formatExpiresString(aItem.expires), - isDomain: aItem.isDomain ? this._bundle.getString("domainColon") - : this._bundle.getString("hostColon"), - isSecure: aItem.isSecure ? this._bundle.getString("forSecureOnly") - : this._bundle.getString("forAnyConnection") }; - for (var i = 0; i < ids.length; ++i) - document.getElementById(ids[i]).disabled = false; - } - else { - var noneSelected = this._bundle.getString("noCookieSelected"); - properties = { name: noneSelected, value: noneSelected, host: noneSelected, - path: noneSelected, expires: noneSelected, - isSecure: noneSelected }; - for (i = 0; i < ids.length; ++i) - document.getElementById(ids[i]).disabled = true; - } - for (var property in properties) - document.getElementById(property).value = properties[property]; - }, - - onCookieSelected: function () { - var properties, item; - var seln = this._tree.view.selection; - var hasRows = this._tree.view.rowCount > 0; - var hasSelection = seln.count > 0; - if (!this._view._filtered) - item = this._view._getItemAtIndex(seln.currentIndex); - else - item = this._view._filterSet[seln.currentIndex]; - - this._updateCookieData(item); - - var rangeCount = seln.getRangeCount(); - var selectedCookieCount = 0; - for (var i = 0; i < rangeCount; ++i) { - var min = {}; var max = {}; - seln.getRangeAt(i, min, max); - for (var j = min.value; j <= max.value; ++j) { - item = this._view._getItemAtIndex(j); - if (!item) continue; - if (item.container && !item.open) - selectedCookieCount += item.cookies.length; - else if (!item.container) - ++selectedCookieCount; - } - } - var item = this._view._getItemAtIndex(seln.currentIndex); - if (item && seln.count == 1 && item.container && item.open) - selectedCookieCount += 2; - - let buttonLabel = this._bundle.getString("removeSelectedCookies.label"); - let removeSelectedCookies = document.getElementById("removeSelectedCookies"); - removeSelectedCookies.label = PluralForm.get(selectedCookieCount, buttonLabel) - .replace("#1", selectedCookieCount); - - removeSelectedCookies.disabled = !hasRows || !hasSelection; - }, - - performDeletion: function gCookiesWindow_performDeletion(deleteItems) { - var psvc = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - var blockFutureCookies = false; - if (psvc.prefHasUserValue("network.cookie.blockFutureCookies")) - blockFutureCookies = psvc.getBoolPref("network.cookie.blockFutureCookies"); - for (var i = 0; i < deleteItems.length; ++i) { - var item = deleteItems[i]; - this._cm.remove(item.host, item.name, item.path, - blockFutureCookies, item.originAttributes); - } - }, - - deleteCookie: function () { - // Selection Notes - // - Selection always moves to *NEXT* adjacent item unless item - // is last child at a given level in which case it moves to *PREVIOUS* - // item - // - // Selection Cases (Somewhat Complicated) - // - // 1) Single cookie selected, host has single child - // v cnn.com - // //// cnn.com ///////////// goksdjf@ //// - // > atwola.com - // - // Before SelectedIndex: 1 Before RowCount: 3 - // After SelectedIndex: 0 After RowCount: 1 - // - // 2) Host selected, host open - // v goats.com //////////////////////////// - // goats.com sldkkfjl - // goat.scom flksj133 - // > atwola.com - // - // Before SelectedIndex: 0 Before RowCount: 4 - // After SelectedIndex: 0 After RowCount: 1 - // - // 3) Host selected, host closed - // > goats.com //////////////////////////// - // > atwola.com - // - // Before SelectedIndex: 0 Before RowCount: 2 - // After SelectedIndex: 0 After RowCount: 1 - // - // 4) Single cookie selected, host has many children - // v goats.com - // goats.com sldkkfjl - // //// goats.com /////////// flksjl33 //// - // > atwola.com - // - // Before SelectedIndex: 2 Before RowCount: 4 - // After SelectedIndex: 1 After RowCount: 3 - // - // 5) Single cookie selected, host has many children - // v goats.com - // //// goats.com /////////// flksjl33 //// - // goats.com sldkkfjl - // > atwola.com - // - // Before SelectedIndex: 1 Before RowCount: 4 - // After SelectedIndex: 1 After RowCount: 3 - var seln = this._view.selection; - var tbo = this._tree.treeBoxObject; - - if (seln.count < 1) return; - - var nextSelected = 0; - var rowCountImpact = 0; - var deleteItems = []; - if (!this._view._filtered) { - var ci = seln.currentIndex; - nextSelected = ci; - var invalidateRow = -1; - var item = this._view._getItemAtIndex(ci); - if (item.container) { - rowCountImpact -= (item.open ? item.cookies.length : 0) + 1; - deleteItems = deleteItems.concat(item.cookies); - if (!this._view.hasNextSibling(-1, ci)) - --nextSelected; - this._view._removeItemAtIndex(ci); - } - else { - var parent = this._view._getItemAtIndex(item.parentIndex); - --rowCountImpact; - if (parent.cookies.length == 1) { - --rowCountImpact; - deleteItems.push(item); - if (!this._view.hasNextSibling(-1, ci)) - --nextSelected; - if (!this._view.hasNextSibling(-1, item.parentIndex)) - --nextSelected; - this._view._removeItemAtIndex(item.parentIndex); - invalidateRow = item.parentIndex; - } - else { - deleteItems.push(item); - if (!this._view.hasNextSibling(-1, ci)) - --nextSelected; - this._view._removeItemAtIndex(ci); - } - } - this._view._rowCount += rowCountImpact; - tbo.rowCountChanged(ci, rowCountImpact); - if (invalidateRow != -1) - tbo.invalidateRow(invalidateRow); - } - else { - var rangeCount = seln.getRangeCount(); - // Traverse backwards through selections to avoid messing - // up the indices when they are deleted. - // See bug 388079. - for (var i = rangeCount - 1; i >= 0; --i) { - var min = {}; var max = {}; - seln.getRangeAt(i, min, max); - nextSelected = min.value; - for (var j = min.value; j <= max.value; ++j) { - deleteItems.push(this._view._getItemAtIndex(j)); - if (!this._view.hasNextSibling(-1, max.value)) - --nextSelected; - } - var delta = max.value - min.value + 1; - this._view._removeItemAtIndex(min.value, delta); - rowCountImpact = -1 * delta; - this._view._rowCount += rowCountImpact; - tbo.rowCountChanged(min.value, rowCountImpact); - } - } - - this.performDeletion(deleteItems); - - if (nextSelected < 0) - seln.clearSelection(); - else { - seln.select(nextSelected); - this._tree.focus(); - } - }, - - deleteAllCookies: function () { - if (this._view._filtered) { - var rowCount = this._view.rowCount; - var deleteItems = []; - for (var index = 0; index < rowCount; index++) { - deleteItems.push(this._view._getItemAtIndex(index)); - } - this._view._removeItemAtIndex(0, rowCount); - this._view._rowCount = 0; - this._tree.treeBoxObject.rowCountChanged(0, -rowCount); - this.performDeletion(deleteItems); - } - else { - this._cm.removeAll(); - } - this._updateRemoveAllButton(); - this.focusFilterBox(); - }, - - onCookieKeyPress: function (aEvent) { - if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE -#ifdef XP_MACOSX - || aEvent.keyCode == KeyEvent.DOM_VK_BACK_SPACE -#endif - ) { - this.deleteCookie(); - } - }, - - _lastSortProperty : "", - _lastSortAscending: false, - sort: function (aProperty) { - var ascending = (aProperty == this._lastSortProperty) ? !this._lastSortAscending : true; - // Sort the Non-Filtered Host Collections - if (aProperty == "rawHost") { - function sortByHost(a, b) { - return a.toLowerCase().localeCompare(b.toLowerCase()); - } - this._hostOrder.sort(sortByHost); - if (!ascending) - this._hostOrder.reverse(); - } - - function sortByProperty(a, b) { - return a[aProperty].toLowerCase().localeCompare(b[aProperty].toLowerCase()); - } - for (var host in this._hosts) { - var cookies = this._hosts[host].cookies; - cookies.sort(sortByProperty); - if (!ascending) - cookies.reverse(); - } - // Sort the Filtered List, if in Filtered mode - if (this._view._filtered) { - this._view._filterSet.sort(sortByProperty); - if (!ascending) - this._view._filterSet.reverse(); - } - - // Adjust the Sort Indicator - var domainCol = document.getElementById("domainCol"); - var nameCol = document.getElementById("nameCol"); - var sortOrderString = ascending ? "ascending" : "descending"; - if (aProperty == "rawHost") { - domainCol.setAttribute("sortDirection", sortOrderString); - nameCol.removeAttribute("sortDirection"); - } - else { - nameCol.setAttribute("sortDirection", sortOrderString); - domainCol.removeAttribute("sortDirection"); - } - - this._view._invalidateCache(0); - this._view.selection.clearSelection(); - if (this._view.rowCount > 0) { - this._view.selection.select(0); - } - this._tree.treeBoxObject.invalidate(); - this._tree.treeBoxObject.ensureRowIsVisible(0); - - this._lastSortAscending = ascending; - this._lastSortProperty = aProperty; - }, - - clearFilter: function () { - // Revert to single-select in the tree - this._tree.setAttribute("seltype", "single"); - - // Clear the Tree Display - this._view._filtered = false; - this._view._rowCount = 0; - this._tree.treeBoxObject.rowCountChanged(0, -this._view._filterSet.length); - this._view._filterSet = []; - - // Just reload the list to make sure deletions are respected - this._loadCookies(); - this._tree.view = this._view; - - // Restore sort order - var sortby = this._lastSortProperty; - if (sortby == "") { - this._lastSortAscending = false; - this.sort("rawHost"); - } - else { - this._lastSortAscending = !this._lastSortAscending; - this.sort(sortby); - } - - // Restore open state - for (var i = 0; i < this._openIndices.length; ++i) - this._view.toggleOpenState(this._openIndices[i]); - this._openIndices = []; - - // Restore selection - this._view.selection.clearSelection(); - for (i = 0; i < this._lastSelectedRanges.length; ++i) { - var range = this._lastSelectedRanges[i]; - this._view.selection.rangedSelect(range.min, range.max, true); - } - this._lastSelectedRanges = []; - - document.getElementById("cookiesIntro").value = this._bundle.getString("cookiesAll"); - this._updateRemoveAllButton(); - }, - - _cookieMatchesFilter: function (aCookie) { - return aCookie.rawHost.indexOf(this._view._filterValue) != -1 || - aCookie.name.indexOf(this._view._filterValue) != -1 || - aCookie.value.indexOf(this._view._filterValue) != -1; - }, - - _filterCookies: function (aFilterValue) { - this._view._filterValue = aFilterValue; - var cookies = []; - for (var i = 0; i < gCookiesWindow._hostOrder.length; ++i) { //var host in gCookiesWindow._hosts) { - var currHost = gCookiesWindow._hosts[gCookiesWindow._hostOrder[i]]; // gCookiesWindow._hosts[host]; - if (!currHost) continue; - for (var j = 0; j < currHost.cookies.length; ++j) { - var cookie = currHost.cookies[j]; - if (this._cookieMatchesFilter(cookie)) - cookies.push(cookie); - } - } - return cookies; - }, - - _lastSelectedRanges: [], - _openIndices: [], - _saveState: function () { - // Save selection - var seln = this._view.selection; - this._lastSelectedRanges = []; - var rangeCount = seln.getRangeCount(); - for (var i = 0; i < rangeCount; ++i) { - var min = {}; var max = {}; - seln.getRangeAt(i, min, max); - this._lastSelectedRanges.push({ min: min.value, max: max.value }); - } - - // Save open states - this._openIndices = []; - for (i = 0; i < this._view.rowCount; ++i) { - var item = this._view._getItemAtIndex(i); - if (item && item.container && item.open) - this._openIndices.push(i); - } - }, - - _updateRemoveAllButton: function gCookiesWindow__updateRemoveAllButton() { - let removeAllCookies = document.getElementById("removeAllCookies"); - removeAllCookies.disabled = this._view._rowCount == 0; - - let labelStringID = "removeAllCookies.label"; - let accessKeyStringID = "removeAllCookies.accesskey"; - if (this._view._filtered) { - labelStringID = "removeAllShownCookies.label"; - accessKeyStringID = "removeAllShownCookies.accesskey"; - } - removeAllCookies.setAttribute("label", this._bundle.getString(labelStringID)); - removeAllCookies.setAttribute("accesskey", this._bundle.getString(accessKeyStringID)); - }, - - filter: function () { - var filter = document.getElementById("filter").value; - if (filter == "") { - gCookiesWindow.clearFilter(); - return; - } - var view = gCookiesWindow._view; - view._filterSet = gCookiesWindow._filterCookies(filter); - if (!view._filtered) { - // Save Display Info for the Non-Filtered mode when we first - // enter Filtered mode. - gCookiesWindow._saveState(); - view._filtered = true; - } - // Move to multi-select in the tree - gCookiesWindow._tree.setAttribute("seltype", "multiple"); - - // Clear the display - var oldCount = view._rowCount; - view._rowCount = 0; - gCookiesWindow._tree.treeBoxObject.rowCountChanged(0, -oldCount); - // Set up the filtered display - view._rowCount = view._filterSet.length; - gCookiesWindow._tree.treeBoxObject.rowCountChanged(0, view.rowCount); - - // if the view is not empty then select the first item - if (view.rowCount > 0) - view.selection.select(0); - - document.getElementById("cookiesIntro").value = gCookiesWindow._bundle.getString("cookiesFiltered"); - this._updateRemoveAllButton(); - }, - - setFilter: function (aFilterString) { - document.getElementById("filter").value = aFilterString; - this.filter(); - }, - - focusFilterBox: function () { - var filter = document.getElementById("filter"); - filter.focus(); - filter.select(); - }, - - onWindowKeyPress: function (aEvent) { - if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE) - window.close(); - } -}; diff --git a/components/preferences/cookies.xul b/components/preferences/cookies.xul deleted file mode 100644 index 60725e9..0000000 --- a/components/preferences/cookies.xul +++ /dev/null @@ -1,106 +0,0 @@ -<?xml version="1.0"?> - -# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> -<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?> - -<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/cookies.dtd" > - -<window id="CookiesDialog" windowtype="Browser:Cookies" - class="windowDialog" title="&window.title;" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - style="width: &window.width;;" - onload="gCookiesWindow.init();" - onunload="gCookiesWindow.uninit();" - persist="screenX screenY width height" - onkeypress="gCookiesWindow.onWindowKeyPress(event);"> - - <script src="chrome://browser/content/preferences/cookies.js"/> - - <stringbundle id="bundlePreferences" - src="chrome://browser/locale/preferences/preferences.properties"/> - - <keyset> - <key key="&windowClose.key;" modifiers="accel" oncommand="window.close();"/> - <key key="&focusSearch1.key;" modifiers="accel" oncommand="gCookiesWindow.focusFilterBox();"/> - <key key="&focusSearch2.key;" modifiers="accel" oncommand="gCookiesWindow.focusFilterBox();"/> - </keyset> - - <vbox flex="1" class="contentPane"> - <hbox align="center"> - <label accesskey="&filter.accesskey;" control="filter">&filter.label;</label> - <textbox type="search" id="filter" flex="1" - aria-controls="cookiesList" - oncommand="gCookiesWindow.filter();"/> - </hbox> - <separator class="thin"/> - <label control="cookiesList" id="cookiesIntro" value="&cookiesonsystem.label;"/> - <separator class="thin"/> - <tree id="cookiesList" flex="1" style="height: 10em;" - onkeypress="gCookiesWindow.onCookieKeyPress(event)" - onselect="gCookiesWindow.onCookieSelected();" - hidecolumnpicker="true" seltype="single"> - <treecols> - <treecol id="domainCol" label="&cookiedomain.label;" flex="2" primary="true" - persist="width" onclick="gCookiesWindow.sort('rawHost');"/> - <splitter class="tree-splitter"/> - <treecol id="nameCol" label="&cookiename.label;" flex="1" - persist="width" - onclick="gCookiesWindow.sort('name');"/> - </treecols> - <treechildren id="cookiesChildren"/> - </tree> - <hbox id="cookieInfoBox"> - <grid flex="1" id="cookieInfoGrid"> - <columns> - <column/> - <column flex="1"/> - </columns> - <rows> - <row align="center"> - <hbox pack="end"><label id="nameLabel" control="name" value="&props.name.label;"/></hbox> - <textbox id="name" readonly="true" class="plain"/> - </row> - <row align="center"> - <hbox pack="end"><label id="valueLabel" control="value" value="&props.value.label;"/></hbox> - <textbox id="value" readonly="true" class="plain"/> - </row> - <row align="center"> - <hbox pack="end"><label id="isDomain" control="host" value="&props.domain.label;"/></hbox> - <textbox id="host" readonly="true" class="plain"/> - </row> - <row align="center"> - <hbox pack="end"><label id="pathLabel" control="path" value="&props.path.label;"/></hbox> - <textbox id="path" readonly="true" class="plain"/> - </row> - <row align="center"> - <hbox pack="end"><label id="isSecureLabel" control="isSecure" value="&props.secure.label;"/></hbox> - <textbox id="isSecure" readonly="true" class="plain"/> - </row> - <row align="center"> - <hbox pack="end"><label id="expiresLabel" control="expires" value="&props.expires.label;"/></hbox> - <textbox id="expires" readonly="true" class="plain"/> - </row> - </rows> - </grid> - </hbox> - </vbox> - <hbox align="end"> - <hbox class="actionButtons" flex="1"> - <button id="removeSelectedCookies" disabled="true" icon="clear" - oncommand="gCookiesWindow.deleteCookie();"/> - <button id="removeAllCookies" disabled="true" icon="clear" - oncommand="gCookiesWindow.deleteAllCookies();"/> - <spacer flex="1"/> -#ifndef XP_MACOSX - <button oncommand="close();" icon="close" - label="&button.close.label;" accesskey="&button.close.accesskey;"/> -#endif - </hbox> - <resizer type="window" dir="bottomend"/> - </hbox> -</window> diff --git a/components/preferences/fonts.js b/components/preferences/fonts.js deleted file mode 100644 index e9f93a2..0000000 --- a/components/preferences/fonts.js +++ /dev/null @@ -1,144 +0,0 @@ -/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// browser.display.languageList LOCK ALL when LOCKED - -const kDefaultFontType = "font.default.%LANG%"; -const kFontNameFmtSerif = "font.name.serif.%LANG%"; -const kFontNameFmtSansSerif = "font.name.sans-serif.%LANG%"; -const kFontNameFmtMonospace = "font.name.monospace.%LANG%"; -const kFontNameListFmtSerif = "font.name-list.serif.%LANG%"; -const kFontNameListFmtSansSerif = "font.name-list.sans-serif.%LANG%"; -const kFontNameListFmtMonospace = "font.name-list.monospace.%LANG%"; -const kFontSizeFmtVariable = "font.size.variable.%LANG%"; -const kFontSizeFmtFixed = "font.size.fixed.%LANG%"; -const kFontMinSizeFmt = "font.minimum-size.%LANG%"; - -var gFontsDialog = { - _selectLanguageGroup: function (aLanguageGroup) - { - var prefs = [{ format: kDefaultFontType, type: "string", element: "defaultFontType", fonttype: null}, - { format: kFontNameFmtSerif, type: "fontname", element: "serif", fonttype: "serif" }, - { format: kFontNameFmtSansSerif, type: "fontname", element: "sans-serif", fonttype: "sans-serif" }, - { format: kFontNameFmtMonospace, type: "fontname", element: "monospace", fonttype: "monospace" }, - { format: kFontNameListFmtSerif, type: "unichar", element: null, fonttype: "serif" }, - { format: kFontNameListFmtSansSerif, type: "unichar", element: null, fonttype: "sans-serif" }, - { format: kFontNameListFmtMonospace, type: "unichar", element: null, fonttype: "monospace" }, - { format: kFontSizeFmtVariable, type: "int", element: "sizeVar", fonttype: null }, - { format: kFontSizeFmtFixed, type: "int", element: "sizeMono", fonttype: null }, - { format: kFontMinSizeFmt, type: "int", element: "minSize", fonttype: null }]; - var preferences = document.getElementById("fontPreferences"); - for (var i = 0; i < prefs.length; ++i) { - var preference = document.getElementById(prefs[i].format.replace(/%LANG%/, aLanguageGroup)); - if (!preference) { - preference = document.createElement("preference"); - var name = prefs[i].format.replace(/%LANG%/, aLanguageGroup); - preference.id = name; - preference.setAttribute("name", name); - preference.setAttribute("type", prefs[i].type); - preferences.appendChild(preference); - } - - if (!prefs[i].element) - continue; - - var element = document.getElementById(prefs[i].element); - if (element) { - element.setAttribute("preference", preference.id); - - if (prefs[i].fonttype) - FontBuilder.buildFontList(aLanguageGroup, prefs[i].fonttype, element); - - preference.setElementValue(element); - } - } - }, - - readFontLanguageGroup: function () - { - var languagePref = document.getElementById("font.language.group"); - this._selectLanguageGroup(languagePref.value); - return undefined; - }, - - readFontSelection: function (aElement) - { - // Determine the appropriate value to select, for the following cases: - // - there is no setting - // - the font selected by the user is no longer present (e.g. deleted from - // fonts folder) - var preference = document.getElementById(aElement.getAttribute("preference")); - if (preference.value) { - var fontItems = aElement.getElementsByAttribute("value", preference.value); - - // There is a setting that actually is in the list. Respect it. - if (fontItems.length > 0) - return undefined; - } - - var defaultValue = aElement.firstChild.firstChild.getAttribute("value"); - var languagePref = document.getElementById("font.language.group"); - preference = document.getElementById("font.name-list." + aElement.id + "." + languagePref.value); - if (!preference || !preference.hasUserValue) - return defaultValue; - - var fontNames = preference.value.split(","); - var stripWhitespace = /^\s*(.*)\s*$/; - - for (var i = 0; i < fontNames.length; ++i) { - var fontName = fontNames[i].replace(stripWhitespace, "$1"); - fontItems = aElement.getElementsByAttribute("value", fontName); - if (fontItems.length) - break; - } - if (fontItems.length) - return fontItems[0].getAttribute("value"); - return defaultValue; - }, - - readUseDocumentFonts: function () - { - var preference = document.getElementById("browser.display.use_document_fonts"); - return preference.value == 1; - }, - - writeUseDocumentFonts: function () - { - var useDocumentFonts = document.getElementById("useDocumentFonts"); - return useDocumentFonts.checked ? 1 : 0; - }, - - onBeforeAccept: function () - { - // Only care in in-content prefs - if (!window.frameElement) { - return true; - } - - let preferences = document.querySelectorAll("preference[id*='font.minimum-size']"); - // It would be good if we could avoid touching languages the pref pages won't use, but - // unfortunately the language group APIs (deducing language groups from language codes) - // are C++ - only. So we just check all the things the user touched: - // Don't care about anything up to 24px, or if this value is the same as set previously: - preferences = Array.filter(preferences, prefEl => { - return prefEl.value > 24 && prefEl.value != prefEl.valueFromPreferences; - }); - if (!preferences.length) { - return; - } - - let strings = document.getElementById("bundlePreferences"); - let title = strings.getString("veryLargeMinimumFontTitle"); - let confirmLabel = strings.getString("acceptVeryLargeMinimumFont"); - let warningMessage = strings.getString("veryLargeMinimumFontWarning"); - let {Services} = Components.utils.import("resource://gre/modules/Services.jsm", {}); - let flags = Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_CANCEL | - Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_IS_STRING | - Services.prompt.BUTTON_POS_1_DEFAULT; - let buttonChosen = Services.prompt.confirmEx(window, title, warningMessage, flags, confirmLabel, null, "", "", {}); - return buttonChosen == 0; - }, -}; - diff --git a/components/preferences/fonts.xul b/components/preferences/fonts.xul deleted file mode 100644 index 97c6869..0000000 --- a/components/preferences/fonts.xul +++ /dev/null @@ -1,279 +0,0 @@ -<?xml version="1.0"?> - -# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> -#ifdef XP_MACOSX -<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?> -#endif - -<!DOCTYPE prefwindow SYSTEM "chrome://browser/locale/preferences/fonts.dtd" > - -<prefwindow id="FontsDialog" type="child" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - title="&fontsDialog.title;" - dlgbuttons="accept,cancel,help" - ondialoghelp="openPrefsHelp()" - onbeforeaccept="return gFontsDialog.onBeforeAccept();" - style=""> - - <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/> - - <prefpane id="FontsDialogPane" - class="largeDialogContainer" - helpTopic="prefs-fonts-and-colors"> - - <preferences id="fontPreferences"> - <preference id="font.language.group" name="font.language.group" type="wstring"/> - <preference id="browser.display.use_document_fonts" - name="browser.display.use_document_fonts" - type="int"/> - <preference id="intl.charset.fallback.override" name="intl.charset.fallback.override" type="string"/> - </preferences> - - <stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/> - <script type="application/javascript" src="chrome://mozapps/content/preferences/fontbuilder.js"/> - <script type="application/javascript" src="chrome://browser/content/preferences/fonts.js"/> - - <!-- Fonts for: [ Language ] --> - <groupbox> - <caption> - <hbox align="center"> - <label accesskey="&language.accesskey;" control="selectLangs">&language.label;</label> - </hbox> - <menulist id="selectLangs" preference="font.language.group" - onsyncfrompreference="return gFontsDialog.readFontLanguageGroup();"> - <menupopup> - <menuitem value="ar" label="&font.langGroup.arabic;"/> - <menuitem value="x-armn" label="&font.langGroup.armenian;"/> - <menuitem value="x-beng" label="&font.langGroup.bengali;"/> - <menuitem value="zh-CN" label="&font.langGroup.simpl-chinese;"/> - <menuitem value="zh-HK" label="&font.langGroup.trad-chinese-hk;"/> - <menuitem value="zh-TW" label="&font.langGroup.trad-chinese;"/> - <menuitem value="x-cyrillic" label="&font.langGroup.cyrillic;"/> - <menuitem value="x-devanagari" label="&font.langGroup.devanagari;"/> - <menuitem value="x-ethi" label="&font.langGroup.ethiopic;"/> - <menuitem value="x-geor" label="&font.langGroup.georgian;"/> - <menuitem value="el" label="&font.langGroup.el;"/> - <menuitem value="x-gujr" label="&font.langGroup.gujarati;"/> - <menuitem value="x-guru" label="&font.langGroup.gurmukhi;"/> - <menuitem value="he" label="&font.langGroup.hebrew;"/> - <menuitem value="ja" label="&font.langGroup.japanese;"/> - <menuitem value="x-knda" label="&font.langGroup.kannada;"/> - <menuitem value="x-khmr" label="&font.langGroup.khmer;"/> - <menuitem value="ko" label="&font.langGroup.korean;"/> - <menuitem value="x-western" label="&font.langGroup.latin;"/> - <menuitem value="x-mlym" label="&font.langGroup.malayalam;"/> - <menuitem value="x-orya" label="&font.langGroup.oriya;"/> - <menuitem value="x-sinh" label="&font.langGroup.sinhala;"/> - <menuitem value="x-tamil" label="&font.langGroup.tamil;"/> - <menuitem value="x-telu" label="&font.langGroup.telugu;"/> - <menuitem value="th" label="&font.langGroup.thai;"/> - <menuitem value="x-tibt" label="&font.langGroup.tibetan;"/> - <menuitem value="x-cans" label="&font.langGroup.canadian;"/> - <menuitem value="x-unicode" label="&font.langGroup.other;"/> - </menupopup> - </menulist> - </caption> - - <grid> - <columns> - <column/> - <column flex="1"/> - <column/> - <column/> - </columns> - - <rows> - <row> - <separator class="thin"/> - </row> - - <row align="center"> - <hbox align="center" pack="end"> - <label accesskey="&proportional.accesskey;" control="defaultFontType">&proportional.label;</label> - </hbox> - <menulist id="defaultFontType" flex="1" style="width: 0px;"> - <menupopup> - <menuitem value="serif" label="&useDefaultFontSerif.label;"/> - <menuitem value="sans-serif" label="&useDefaultFontSansSerif.label;"/> - </menupopup> - </menulist> - <hbox align="center" pack="end"> - <label value="&size.label;" - accesskey="&sizeProportional.accesskey;" - control="sizeVar"/> - </hbox> - <menulist id="sizeVar"> - <menupopup> - <menuitem value="9" label="9"/> - <menuitem value="10" label="10"/> - <menuitem value="11" label="11"/> - <menuitem value="12" label="12"/> - <menuitem value="13" label="13"/> - <menuitem value="14" label="14"/> - <menuitem value="15" label="15"/> - <menuitem value="16" label="16"/> - <menuitem value="17" label="17"/> - <menuitem value="18" label="18"/> - <menuitem value="20" label="20"/> - <menuitem value="22" label="22"/> - <menuitem value="24" label="24"/> - <menuitem value="26" label="26"/> - <menuitem value="28" label="28"/> - <menuitem value="30" label="30"/> - <menuitem value="32" label="32"/> - <menuitem value="34" label="34"/> - <menuitem value="36" label="36"/> - <menuitem value="40" label="40"/> - <menuitem value="44" label="44"/> - <menuitem value="48" label="48"/> - <menuitem value="56" label="56"/> - <menuitem value="64" label="64"/> - <menuitem value="72" label="72"/> - </menupopup> - </menulist> - </row> - <row align="center"> - <hbox align="center" pack="end"> - <label accesskey="&serif.accesskey;" control="serif">&serif.label;</label> - </hbox> - <menulist id="serif" flex="1" style="width: 0px;" - onsyncfrompreference="return gFontsDialog.readFontSelection(document.getElementById('serif'));"/> - <spacer/> - </row> - <row align="center"> - <hbox align="center" pack="end"> - <label accesskey="&sans-serif.accesskey;" control="sans-serif">&sans-serif.label;</label> - </hbox> - <menulist id="sans-serif" flex="1" style="width: 0px;" - onsyncfrompreference="return gFontsDialog.readFontSelection(document.getElementById('sans-serif'));"/> - <spacer/> - </row> - <row align="center"> - <hbox align="center" pack="end"> - <label accesskey="&monospace.accesskey;" control="monospace">&monospace.label;</label> - </hbox> - <menulist id="monospace" flex="1" style="width: 0px;" crop="right" - onsyncfrompreference="return gFontsDialog.readFontSelection(document.getElementById('monospace'));"/> - <hbox align="center" pack="end"> - <label value="&size.label;" - accesskey="&sizeMonospace.accesskey;" - control="sizeMono"/> - </hbox> - <menulist id="sizeMono"> - <menupopup> - <menuitem value="9" label="9"/> - <menuitem value="10" label="10"/> - <menuitem value="11" label="11"/> - <menuitem value="12" label="12"/> - <menuitem value="13" label="13"/> - <menuitem value="14" label="14"/> - <menuitem value="15" label="15"/> - <menuitem value="16" label="16"/> - <menuitem value="17" label="17"/> - <menuitem value="18" label="18"/> - <menuitem value="20" label="20"/> - <menuitem value="22" label="22"/> - <menuitem value="24" label="24"/> - <menuitem value="26" label="26"/> - <menuitem value="28" label="28"/> - <menuitem value="30" label="30"/> - <menuitem value="32" label="32"/> - <menuitem value="34" label="34"/> - <menuitem value="36" label="36"/> - <menuitem value="40" label="40"/> - <menuitem value="44" label="44"/> - <menuitem value="48" label="48"/> - <menuitem value="56" label="56"/> - <menuitem value="64" label="64"/> - <menuitem value="72" label="72"/> - </menupopup> - </menulist> - </row> - </rows> - </grid> - <separator class="thin"/> - <hbox flex="1"> - <spacer flex="1"/> - <hbox align="center" pack="end"> - <label accesskey="&minSize.accesskey;" control="minSize">&minSize.label;</label> - <menulist id="minSize"> - <menupopup> - <menuitem value="0" label="&minSize.none;"/> - <menuitem value="9" label="9"/> - <menuitem value="10" label="10"/> - <menuitem value="11" label="11"/> - <menuitem value="12" label="12"/> - <menuitem value="13" label="13"/> - <menuitem value="14" label="14"/> - <menuitem value="15" label="15"/> - <menuitem value="16" label="16"/> - <menuitem value="17" label="17"/> - <menuitem value="18" label="18"/> - <menuitem value="20" label="20"/> - <menuitem value="22" label="22"/> - <menuitem value="24" label="24"/> - <menuitem value="26" label="26"/> - <menuitem value="28" label="28"/> - <menuitem value="30" label="30"/> - <menuitem value="32" label="32"/> - <menuitem value="34" label="34"/> - <menuitem value="36" label="36"/> - <menuitem value="40" label="40"/> - <menuitem value="44" label="44"/> - <menuitem value="48" label="48"/> - <menuitem value="56" label="56"/> - <menuitem value="64" label="64"/> - <menuitem value="72" label="72"/> - </menupopup> - </menulist> - </hbox> - </hbox> - <separator/> - <separator class="groove"/> - <hbox> - <checkbox id="useDocumentFonts" - label="&allowPagesToUse.label;" accesskey="&allowPagesToUse.accesskey;" - preference="browser.display.use_document_fonts" - onsyncfrompreference="return gFontsDialog.readUseDocumentFonts();" - onsynctopreference="return gFontsDialog.writeUseDocumentFonts();"/> - </hbox> - </groupbox> - - <!-- Character Encoding --> - <groupbox> - <caption label="&languages.customize.Fallback.grouplabel;"/> - <description>&languages.customize.Fallback.desc;</description> - <hbox align="center"> - <label value="&languages.customize.Fallback.label;" - accesskey="&languages.customize.Fallback.accesskey;" - control="DefaultCharsetList"/> - <menulist id="DefaultCharsetList" preference="intl.charset.fallback.override"> - <menupopup> - <menuitem label="&languages.customize.Fallback.auto;" value="*"/> - <menuitem label="&languages.customize.Fallback.utf8;" value="UTF-8"/> - <menuitem label="&languages.customize.Fallback.arabic;" value="windows-1256"/> - <menuitem label="&languages.customize.Fallback.baltic;" value="windows-1257"/> - <menuitem label="&languages.customize.Fallback.ceiso;" value="ISO-8859-2"/> - <menuitem label="&languages.customize.Fallback.cewindows;" value="windows-1250"/> - <menuitem label="&languages.customize.Fallback.simplified;" value="gbk"/> - <menuitem label="&languages.customize.Fallback.traditional;" value="Big5"/> - <menuitem label="&languages.customize.Fallback.cyrillic;" value="windows-1251"/> - <menuitem label="&languages.customize.Fallback.greek;" value="ISO-8859-7"/> - <menuitem label="&languages.customize.Fallback.hebrew;" value="windows-1255"/> - <menuitem label="&languages.customize.Fallback.japanese;" value="Shift_JIS"/> - <menuitem label="&languages.customize.Fallback.korean;" value="EUC-KR"/> - <menuitem label="&languages.customize.Fallback.thai;" value="windows-874"/> - <menuitem label="&languages.customize.Fallback.turkish;" value="windows-1254"/> - <menuitem label="&languages.customize.Fallback.vietnamese;" value="windows-1258"/> - <menuitem label="&languages.customize.Fallback.other;" value="windows-1252"/> - </menupopup> - </menulist> - </hbox> - </groupbox> - </prefpane> -</prefwindow> diff --git a/components/preferences/handlers.css b/components/preferences/handlers.css deleted file mode 100644 index 9a1d474..0000000 --- a/components/preferences/handlers.css +++ /dev/null @@ -1,25 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -richlistitem { - -moz-binding: url("chrome://browser/content/preferences/handlers.xml#handler"); -} - -richlistitem[selected="true"] { - -moz-binding: url("chrome://browser/content/preferences/handlers.xml#handler-selected"); -} - -/** - * Make the icons appear. - * Note: we display the icon box for every item whether or not it has an icon - * so the labels of all the items align vertically. - */ -.actionsMenu > menupopup > menuitem > .menu-iconic-left { - display: -moz-box; - min-width: 16px; -} - -listitem.offlineapp { - -moz-binding: url("chrome://browser/content/preferences/handlers.xml#offlineapp"); -} diff --git a/components/preferences/handlers.xml b/components/preferences/handlers.xml deleted file mode 100644 index 5fb915c..0000000 --- a/components/preferences/handlers.xml +++ /dev/null @@ -1,81 +0,0 @@ -<?xml version="1.0"?> - -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<!DOCTYPE overlay [ - <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> - <!ENTITY % applicationsDTD SYSTEM "chrome://browser/locale/preferences/applications.dtd"> - %brandDTD; - %applicationsDTD; -]> - -<bindings id="handlerBindings" - xmlns="http://www.mozilla.org/xbl" - xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:xbl="http://www.mozilla.org/xbl"> - - <binding id="handler-base" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem"> - <implementation> - <property name="type" readonly="true"> - <getter> - return this.getAttribute("type"); - </getter> - </property> - </implementation> - </binding> - - <binding id="handler" extends="chrome://browser/content/preferences/handlers.xml#handler-base"> - <content> - <xul:hbox flex="1" equalsize="always"> - <xul:hbox flex="1" align="center" xbl:inherits="tooltiptext=typeDescription"> - <xul:image src="moz-icon://goat?size=16" class="typeIcon" - xbl:inherits="src=typeIcon" height="16" width="16"/> - <xul:label flex="1" crop="end" xbl:inherits="value=typeDescription"/> - </xul:hbox> - <xul:hbox flex="1" align="center" xbl:inherits="tooltiptext=actionDescription"> - <xul:image xbl:inherits="src=actionIcon" height="16" width="16" class="actionIcon"/> - <xul:label flex="1" crop="end" xbl:inherits="value=actionDescription"/> - </xul:hbox> - </xul:hbox> - </content> - </binding> - - <binding id="handler-selected" extends="chrome://browser/content/preferences/handlers.xml#handler-base"> - <content> - <xul:hbox flex="1" equalsize="always"> - <xul:hbox flex="1" align="center" xbl:inherits="tooltiptext=typeDescription"> - <xul:image src="moz-icon://goat?size=16" class="typeIcon" - xbl:inherits="src=typeIcon" height="16" width="16"/> - <xul:label flex="1" crop="end" xbl:inherits="value=typeDescription"/> - </xul:hbox> - <xul:hbox flex="1"> - <xul:menulist class="actionsMenu" flex="1" crop="end" selectedIndex="1" - xbl:inherits="tooltiptext=actionDescription" - oncommand="gApplicationsPane.onSelectAction(event.originalTarget)"> - <xul:menupopup/> - </xul:menulist> - </xul:hbox> - </xul:hbox> - </content> - - <implementation> - <constructor> - gApplicationsPane.rebuildActionsMenu(); - </constructor> - </implementation> - - </binding> - - <binding id="offlineapp" - extends="chrome://global/content/bindings/listbox.xml#listitem"> - <content> - <children> - <xul:listcell xbl:inherits="label=origin"/> - <xul:listcell xbl:inherits="label=usage"/> - </children> - </content> - </binding> - -</bindings> diff --git a/components/preferences/jar.mn b/components/preferences/jar.mn deleted file mode 100644 index 6e143de..0000000 --- a/components/preferences/jar.mn +++ /dev/null @@ -1,44 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -browser.jar: -* content/browser/preferences/advanced.xul -* content/browser/preferences/advanced.js - content/browser/preferences/applications.xul -* content/browser/preferences/applications.js - content/browser/preferences/applicationManager.xul -* content/browser/preferences/applicationManager.js -* content/browser/preferences/colors.xul -* content/browser/preferences/cookies.xul -* content/browser/preferences/cookies.js - content/browser/preferences/content.xul - content/browser/preferences/content.js -* content/browser/preferences/connection.xul - content/browser/preferences/connection.js -* content/browser/preferences/fonts.xul - content/browser/preferences/fonts.js - content/browser/preferences/handlers.xml - content/browser/preferences/handlers.css -* content/browser/preferences/languages.xul - content/browser/preferences/languages.js -* content/browser/preferences/main.xul - content/browser/preferences/main.js - content/browser/preferences/newtaburl.js - content/browser/preferences/permissions.xul -* content/browser/preferences/permissions.js -* content/browser/preferences/preferences.xul - content/browser/preferences/privacy.xul - content/browser/preferences/privacy.js - content/browser/preferences/sanitize.xul - content/browser/preferences/sanitize.js - content/browser/preferences/security.xul - content/browser/preferences/security.js - content/browser/preferences/selectBookmark.xul - content/browser/preferences/selectBookmark.js -#ifdef MOZ_SERVICES_SYNC - content/browser/preferences/sync.xul - content/browser/preferences/sync.js -#endif -* content/browser/preferences/tabs.xul -* content/browser/preferences/tabs.js diff --git a/components/preferences/languages.js b/components/preferences/languages.js deleted file mode 100644 index 8d2b394..0000000 --- a/components/preferences/languages.js +++ /dev/null @@ -1,304 +0,0 @@ -/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var gLanguagesDialog = { - - _availableLanguagesList : [], - _acceptLanguages : { }, - - _selectedItemID : null, - - init: function () - { - if (!this._availableLanguagesList.length) - this._loadAvailableLanguages(); - }, - - get _activeLanguages() - { - return document.getElementById("activeLanguages"); - }, - - get _availableLanguages() - { - return document.getElementById("availableLanguages"); - }, - - _loadAvailableLanguages: function () - { - // This is a parser for: resource://gre/res/language.properties - // The file is formatted like so: - // ab[-cd].accept=true|false - // ab = language - // cd = region - var bundleAccepted = document.getElementById("bundleAccepted"); - var bundleRegions = document.getElementById("bundleRegions"); - var bundleLanguages = document.getElementById("bundleLanguages"); - var bundlePreferences = document.getElementById("bundlePreferences"); - - function LanguageInfo(aName, aABCD, aIsVisible) - { - this.name = aName; - this.abcd = aABCD; - this.isVisible = aIsVisible; - } - - // 1) Read the available languages out of language.properties - var strings = bundleAccepted.strings; - while (strings.hasMoreElements()) { - var currString = strings.getNext(); - if (!(currString instanceof Components.interfaces.nsIPropertyElement)) - break; - - var property = currString.key.split("."); // ab[-cd].accept - if (property[1] == "accept") { - var abCD = property[0]; - var abCDPairs = abCD.split("-"); // ab[-cd] - var useABCDFormat = abCDPairs.length > 1; - var ab = useABCDFormat ? abCDPairs[0] : abCD; - var cd = useABCDFormat ? abCDPairs[1] : ""; - if (ab) { - var language = ""; - try { - language = bundleLanguages.getString(ab); - } - catch (e) { continue; }; - - var region = ""; - if (useABCDFormat) { - try { - region = bundleRegions.getString(cd); - } - catch (e) { continue; } - } - - var name = ""; - if (useABCDFormat) - name = bundlePreferences.getFormattedString("languageRegionCodeFormat", - [language, region, abCD]); - else - name = bundlePreferences.getFormattedString("languageCodeFormat", - [language, abCD]); - - if (name && abCD) { - var isVisible = currString.value == "true" && - (!(abCD in this._acceptLanguages) || !this._acceptLanguages[abCD]); - var li = new LanguageInfo(name, abCD, isVisible); - this._availableLanguagesList.push(li); - } - } - } - } - this._buildAvailableLanguageList(); - }, - - _buildAvailableLanguageList: function () - { - var availableLanguagesPopup = document.getElementById("availableLanguagesPopup"); - while (availableLanguagesPopup.hasChildNodes()) - availableLanguagesPopup.removeChild(availableLanguagesPopup.firstChild); - - // Sort the list of languages by name - this._availableLanguagesList.sort(function (a, b) { - return a.name.localeCompare(b.name); - }); - - // Load the UI with the data - for (var i = 0; i < this._availableLanguagesList.length; ++i) { - var abCD = this._availableLanguagesList[i].abcd; - if (this._availableLanguagesList[i].isVisible && - (!(abCD in this._acceptLanguages) || !this._acceptLanguages[abCD])) { - var menuitem = document.createElement("menuitem"); - menuitem.id = this._availableLanguagesList[i].abcd; - availableLanguagesPopup.appendChild(menuitem); - menuitem.setAttribute("label", this._availableLanguagesList[i].name); - } - } - }, - - readAcceptLanguages: function () - { - while (this._activeLanguages.hasChildNodes()) - this._activeLanguages.removeChild(this._activeLanguages.firstChild); - - var selectedIndex = 0; - var preference = document.getElementById("intl.accept_languages"); - if (preference.value == "") - return undefined; - var languages = preference.value.toLowerCase().split(/\s*,\s*/); - for (var i = 0; i < languages.length; ++i) { - var name = this._getLanguageName(languages[i]); - if (!name) - name = "[" + languages[i] + "]"; - var listitem = document.createElement("listitem"); - listitem.id = languages[i]; - if (languages[i] == this._selectedItemID) - selectedIndex = i; - this._activeLanguages.appendChild(listitem); - listitem.setAttribute("label", name); - - // Hash this language as an "Active" language so we don't - // show it in the list that can be added. - this._acceptLanguages[languages[i]] = true; - } - - if (this._activeLanguages.childNodes.length > 0) { - this._activeLanguages.ensureIndexIsVisible(selectedIndex); - this._activeLanguages.selectedIndex = selectedIndex; - } - - return undefined; - }, - - writeAcceptLanguages: function () - { - return undefined; - }, - - onAvailableLanguageSelect: function () - { - var addButton = document.getElementById("addButton"); - addButton.disabled = false; - - this._availableLanguages.removeAttribute("accesskey"); - }, - - addLanguage: function () - { - var selectedID = this._availableLanguages.selectedItem.id; - var preference = document.getElementById("intl.accept_languages"); - var arrayOfPrefs = preference.value.toLowerCase().split(/\s*,\s*/); - for (var i = 0; i < arrayOfPrefs.length; ++i ){ - if (arrayOfPrefs[i] == selectedID) - return; - } - - this._selectedItemID = selectedID; - - if (preference.value == "") - preference.value = selectedID; - else { - arrayOfPrefs.unshift(selectedID); - preference.value = arrayOfPrefs.join(","); - } - - this._acceptLanguages[selectedID] = true; - this._availableLanguages.selectedItem = null; - - // Rebuild the available list with the added item removed... - this._buildAvailableLanguageList(); - - this._availableLanguages.setAttribute("label", this._availableLanguages.getAttribute("label2")); - }, - - removeLanguage: function () - { - // Build the new preference value string. - var languagesArray = []; - for (var i = 0; i < this._activeLanguages.childNodes.length; ++i) { - var item = this._activeLanguages.childNodes[i]; - if (!item.selected) - languagesArray.push(item.id); - else - this._acceptLanguages[item.id] = false; - } - var string = languagesArray.join(","); - - // Get the item to select after the remove operation completes. - var selection = this._activeLanguages.selectedItems; - var lastSelected = selection[selection.length-1]; - var selectItem = lastSelected.nextSibling || lastSelected.previousSibling; - selectItem = selectItem ? selectItem.id : null; - - this._selectedItemID = selectItem; - - // Update the preference and force a UI rebuild - var preference = document.getElementById("intl.accept_languages"); - preference.value = string; - - this._buildAvailableLanguageList(); - }, - - _getLanguageName: function (aABCD) - { - if (!this._availableLanguagesList.length) - this._loadAvailableLanguages(); - for (var i = 0; i < this._availableLanguagesList.length; ++i) { - if (aABCD == this._availableLanguagesList[i].abcd) - return this._availableLanguagesList[i].name; - } - return ""; - }, - - moveUp: function () - { - var selectedItem = this._activeLanguages.selectedItems[0]; - var previousItem = selectedItem.previousSibling; - - var string = ""; - for (var i = 0; i < this._activeLanguages.childNodes.length; ++i) { - var item = this._activeLanguages.childNodes[i]; - string += (i == 0 ? "" : ","); - if (item.id == previousItem.id) - string += selectedItem.id; - else if (item.id == selectedItem.id) - string += previousItem.id; - else - string += item.id; - } - - this._selectedItemID = selectedItem.id; - - // Update the preference and force a UI rebuild - var preference = document.getElementById("intl.accept_languages"); - preference.value = string; - }, - - moveDown: function () - { - var selectedItem = this._activeLanguages.selectedItems[0]; - var nextItem = selectedItem.nextSibling; - - var string = ""; - for (var i = 0; i < this._activeLanguages.childNodes.length; ++i) { - var item = this._activeLanguages.childNodes[i]; - string += (i == 0 ? "" : ","); - if (item.id == nextItem.id) - string += selectedItem.id; - else if (item.id == selectedItem.id) - string += nextItem.id; - else - string += item.id; - } - - this._selectedItemID = selectedItem.id; - - // Update the preference and force a UI rebuild - var preference = document.getElementById("intl.accept_languages"); - preference.value = string; - }, - - onLanguageSelect: function () - { - var upButton = document.getElementById("up"); - var downButton = document.getElementById("down"); - var removeButton = document.getElementById("remove"); - switch (this._activeLanguages.selectedCount) { - case 0: - upButton.disabled = downButton.disabled = removeButton.disabled = true; - break; - case 1: - upButton.disabled = this._activeLanguages.selectedIndex == 0; - downButton.disabled = this._activeLanguages.selectedIndex == this._activeLanguages.childNodes.length - 1; - removeButton.disabled = false; - break; - default: - upButton.disabled = true; - downButton.disabled = true; - removeButton.disabled = false; - } - } -}; - diff --git a/components/preferences/languages.xul b/components/preferences/languages.xul deleted file mode 100644 index 6fd8232..0000000 --- a/components/preferences/languages.xul +++ /dev/null @@ -1,98 +0,0 @@ -<?xml version="1.0"?> - -# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -<!DOCTYPE prefwindow SYSTEM "chrome://browser/locale/preferences/languages.dtd"> - -<?xml-stylesheet href="chrome://global/skin/"?> -#ifdef XP_MACOSX -<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?> -#endif - -<prefwindow id="LanguagesDialog" type="child" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - title="&languages.customize.Header;" - dlgbuttons="accept,cancel,help" - ondialoghelp="openPrefsHelp()" - style="width: &window.width;;"> - - <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/> - - <prefpane id="LanguagesDialogPane" - onpaneload="gLanguagesDialog.init();" - helpTopic="prefs-languages"> - - <preferences> - <preference id="intl.accept_languages" name="intl.accept_languages" type="wstring"/> - <preference id="pref.browser.language.disable_button.up" - name="pref.browser.language.disable_button.up" - type="bool"/> - <preference id="pref.browser.language.disable_button.down" - name="pref.browser.language.disable_button.down" - type="bool"/> - <preference id="pref.browser.language.disable_button.remove" - name="pref.browser.language.disable_button.remove" - type="bool"/> - </preferences> - - <script type="application/javascript" src="chrome://browser/content/preferences/languages.js"/> - - <stringbundleset id="languageSet"> - <stringbundle id="bundleRegions" src="chrome://global/locale/regionNames.properties"/> - <stringbundle id="bundleLanguages" src="chrome://global/locale/languageNames.properties"/> - <stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/> - <stringbundle id="bundleAccepted" src="resource://gre/res/language.properties"/> - </stringbundleset> - - <description>&languages.customize.prefLangDescript;</description> - <label>&languages.customize.active.label;</label> - <grid flex="1"> - <columns> - <column flex="1"/> - <column/> - </columns> - <rows> - <row flex="1"> - <listbox id="activeLanguages" flex="1" rows="6" - seltype="multiple" onselect="gLanguagesDialog.onLanguageSelect();" - preference="intl.accept_languages" - onsyncfrompreference="return gLanguagesDialog.readAcceptLanguages();" - onsynctopreference="return gLanguagesDialog.writeAcceptLanguages();"/> - <vbox> - <button id="up" class="up" oncommand="gLanguagesDialog.moveUp();" disabled="true" - label="&languages.customize.moveUp.label;" - accesskey="&languages.customize.moveUp.accesskey;" - preference="pref.browser.language.disable_button.up"/> - <button id="down" class="down" oncommand="gLanguagesDialog.moveDown();" disabled="true" - label="&languages.customize.moveDown.label;" - accesskey="&languages.customize.moveDown.accesskey;" - preference="pref.browser.language.disable_button.down"/> - <button id="remove" oncommand="gLanguagesDialog.removeLanguage();" disabled="true" - label="&languages.customize.deleteButton.label;" - accesskey="&languages.customize.deleteButton.accesskey;" - preference="pref.browser.language.disable_button.remove"/> - </vbox> - </row> - <row> - <separator class="thin"/> - </row> - <row> - <menulist id="availableLanguages" oncommand="gLanguagesDialog.onAvailableLanguageSelect();" - label="&languages.customize.selectLanguage.label;" - label2="&languages.customize.selectLanguage.label;"> - <menupopup id="availableLanguagesPopup"/> - </menulist> - <button id="addButton" oncommand="gLanguagesDialog.addLanguage();" disabled="true" - label="&languages.customize.addButton.label;" - accesskey="&languages.customize.addButton.accesskey;"/> - </row> - </rows> - </grid> - <separator/> - <separator/> - </prefpane> -</prefwindow> - diff --git a/components/preferences/main.js b/components/preferences/main.js deleted file mode 100644 index 07e09ff..0000000 --- a/components/preferences/main.js +++ /dev/null @@ -1,473 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon", - "resource:///modules/DownloadsCommon.jsm"); - -var gMainPane = { - _pane: null, - - /** - * Initialization of this. - */ - init: function () - { - this._pane = document.getElementById("paneMain"); - - // set up the "use current page" label-changing listener - this._updateUseCurrentButton(); - window.addEventListener("focus", this._updateUseCurrentButton.bind(this), false); - - this.updateBrowserStartupLastSession(); - - this.setupDownloadsWindowOptions(); - - // Notify observers that the UI is now ready - Components.classes["@mozilla.org/observer-service;1"] - .getService(Components.interfaces.nsIObserverService) - .notifyObservers(window, "main-pane-loaded", null); - }, - - setupDownloadsWindowOptions: function () - { - let showWhenDownloading = document.getElementById("showWhenDownloading"); - let closeWhenDone = document.getElementById("closeWhenDone"); - - // These radio buttons should be hidden when the Downloads Panel is enabled. - let shouldHide = !DownloadsCommon.useToolkitUI; - showWhenDownloading.hidden = shouldHide; - closeWhenDone.hidden = shouldHide; - }, - - // HOME PAGE - - /* - * Preferences: - * - * browser.startup.homepage - * - the user's home page, as a string; if the home page is a set of tabs, - * this will be those URLs separated by the pipe character "|" - * browser.startup.page - * - what page(s) to show when the user starts the application, as an integer: - * - * 0: a blank page - * 1: the home page (as set by the browser.startup.homepage pref) - * 2: the last page the user visited (DEPRECATED) - * 3: windows and tabs from the last session (a.k.a. session restore) - * - * The deprecated option is not exposed in UI; however, if the user has it - * selected and doesn't change the UI for this preference, the deprecated - * option is preserved. - */ - - syncFromHomePref: function () - { - let homePref = document.getElementById("browser.startup.homepage"); - - // If the pref is set to about:home, set the value to "" to show the - // placeholder text (about:home title). - if (homePref.value.toLowerCase() == "about:home") - return ""; - - // If the pref is actually "", show a blank page. The actual home page - // loading code treats them the same, and we don't want the placeholder text - // to be shown. - if (homePref.value == "") - return "about:logopage"; - - // Otherwise, show the actual pref value. - return undefined; - }, - - syncToHomePref: function (value) - { - // If the value is "", use about:home. - if (value == "") - return "about:home"; - - // Otherwise, use the actual textbox value. - return undefined; - }, - - /** - * Sets the home page to the current displayed page (or frontmost tab, if the - * most recent browser window contains multiple tabs), updating preference - * window UI to reflect this. - */ - setHomePageToCurrent: function () - { - let homePage = document.getElementById("browser.startup.homepage"); - let tabs = this._getTabsForHomePage(); - function getTabURI(t) t.linkedBrowser.currentURI.spec; - - // FIXME Bug 244192: using dangerous "|" joiner! - if (tabs.length) - homePage.value = tabs.map(getTabURI).join("|"); - }, - - /** - * Displays a dialog in which the user can select a bookmark to use as home - * page. If the user selects a bookmark, that bookmark's name is displayed in - * UI and the bookmark's address is stored to the home page preference. - */ - setHomePageToBookmark: function () - { - var rv = { urls: null, names: null }; - document.documentElement.openSubDialog("chrome://browser/content/preferences/selectBookmark.xul", - "resizable", rv); - if (rv.urls && rv.names) { - var homePage = document.getElementById("browser.startup.homepage"); - - // XXX still using dangerous "|" joiner! - homePage.value = rv.urls.join("|"); - } - }, - - /** - * Switches the "Use Current Page" button between its singular and plural - * forms. - */ - _updateUseCurrentButton: function () { - let useCurrent = document.getElementById("useCurrent"); - - let tabs = this._getTabsForHomePage(); - if (tabs.length > 1) - useCurrent.label = useCurrent.getAttribute("label2"); - else - useCurrent.label = useCurrent.getAttribute("label1"); - - // In this case, the button's disabled state is set by preferences.xml. - if (document.getElementById - ("pref.browser.homepage.disable_button.current_page").locked) - return; - - useCurrent.disabled = !tabs.length - }, - - _getTabsForHomePage: function () - { - var win; - var tabs = []; - if (document.documentElement.instantApply) { - const Cc = Components.classes, Ci = Components.interfaces; - // If we're in instant-apply mode, use the most recent browser window - var wm = Cc["@mozilla.org/appshell/window-mediator;1"] - .getService(Ci.nsIWindowMediator); - win = wm.getMostRecentWindow("navigator:browser"); - } - else { - win = window.opener; - } - - if (win && win.document.documentElement - .getAttribute("windowtype") == "navigator:browser") { - // We should only include visible & non-pinned tabs - tabs = win.gBrowser.visibleTabs.slice(win.gBrowser._numPinnedTabs); - } - - return tabs; - }, - - /** - * Restores the default home page as the user's home page. - */ - restoreDefaultHomePage: function () - { - var homePage = document.getElementById("browser.startup.homepage"); - homePage.value = homePage.defaultValue; - }, - - // DOWNLOADS - - /* - * Preferences: - * - * browser.download.showWhenStarting - bool - * True if the Download Manager should be opened when a download is - * started, false if it shouldn't be opened. - * browser.download.closeWhenDone - bool - * True if the Download Manager should be closed when all downloads - * complete, false if it should be left open. - * browser.download.useDownloadDir - bool - * True - Save files directly to the folder configured via the - * browser.download.folderList preference. - * False - Always ask the user where to save a file and default to - * browser.download.lastDir when displaying a folder picker dialog. - * browser.download.dir - local file handle - * A local folder the user may have selected for downloaded files to be - * saved. Migration of other browser settings may also set this path. - * This folder is enabled when folderList equals 2. - * browser.download.lastDir - local file handle - * May contain the last folder path accessed when the user browsed - * via the file save-as dialog. (see contentAreaUtils.js) - * browser.download.folderList - int - * Indicates the location users wish to save downloaded files too. - * It is also used to display special file labels when the default - * download location is either the Desktop or the Downloads folder. - * Values: - * 0 - The desktop is the default download location. - * 1 - The system's downloads folder is the default download location. - * 2 - The default download location is elsewhere as specified in - * browser.download.dir. - * browser.download.downloadDir - * deprecated. - * browser.download.defaultFolder - * deprecated. - */ - - /** - * Updates preferences which depend upon the value of the preference which - * determines whether the Downloads manager is opened at the start of a - * download. - */ - readShowDownloadsWhenStarting: function () - { - this.showDownloadsWhenStartingPrefChanged(); - - // don't override the preference's value in UI - return undefined; - }, - - /** - * Enables or disables the "close Downloads manager when downloads finished" - * preference element, consequently updating the associated UI. - */ - showDownloadsWhenStartingPrefChanged: function () - { - var showWhenStartingPref = document.getElementById("browser.download.manager.showWhenStarting"); - var closeWhenDonePref = document.getElementById("browser.download.manager.closeWhenDone"); - closeWhenDonePref.disabled = !showWhenStartingPref.value; - }, - - /** - * Enables/disables the folder field and Browse button based on whether a - * default download directory is being used. - */ - readUseDownloadDir: function () - { - var downloadFolder = document.getElementById("downloadFolder"); - var chooseFolder = document.getElementById("chooseFolder"); - var preference = document.getElementById("browser.download.useDownloadDir"); - downloadFolder.disabled = !preference.value; - chooseFolder.disabled = !preference.value; - - // don't override the preference's value in UI - return undefined; - }, - - /** - * Displays a file picker in which the user can choose the location where - * downloads are automatically saved, updating preferences and UI in - * response to the choice, if one is made. - */ - chooseFolder: function () - { - const nsIFilePicker = Components.interfaces.nsIFilePicker; - const nsILocalFile = Components.interfaces.nsILocalFile; - - let bundlePreferences = document.getElementById("bundlePreferences"); - let title = bundlePreferences.getString("chooseDownloadFolderTitle"); - let folderListPref = document.getElementById("browser.download.folderList"); - let currentDirPref = this._indexToFolder(folderListPref.value); // file - let defDownloads = this._indexToFolder(1); // file - let fp = Components.classes["@mozilla.org/filepicker;1"]. - createInstance(nsIFilePicker); - let fpCallback = function fpCallback_done(aResult) { - if (aResult == nsIFilePicker.returnOK) { - let file = fp.file.QueryInterface(nsILocalFile); - let downloadDirPref = document.getElementById("browser.download.dir"); - - downloadDirPref.value = file; - folderListPref.value = this._folderToIndex(file); - // Note, the real prefs will not be updated yet, so dnld manager's - // userDownloadsDirectory may not return the right folder after - // this code executes. displayDownloadDirPref will be called on - // the assignment above to update the UI. - } - }.bind(this); - - fp.init(window, title, nsIFilePicker.modeGetFolder); - fp.appendFilters(nsIFilePicker.filterAll); - // First try to open what's currently configured - if (currentDirPref && currentDirPref.exists()) { - fp.displayDirectory = currentDirPref; - } // Try the system's download dir - else if (defDownloads && defDownloads.exists()) { - fp.displayDirectory = defDownloads; - } // Fall back to Desktop - else { - fp.displayDirectory = this._indexToFolder(0); - } - fp.open(fpCallback); - }, - - /** - * Initializes the download folder display settings based on the user's - * preferences. - */ - displayDownloadDirPref: function () - { - var folderListPref = document.getElementById("browser.download.folderList"); - var bundlePreferences = document.getElementById("bundlePreferences"); - var downloadFolder = document.getElementById("downloadFolder"); - var currentDirPref = document.getElementById("browser.download.dir"); - - // Used in defining the correct path to the folder icon. - var ios = Components.classes["@mozilla.org/network/io-service;1"] - .getService(Components.interfaces.nsIIOService); - var fph = ios.getProtocolHandler("file") - .QueryInterface(Components.interfaces.nsIFileProtocolHandler); - var iconUrlSpec; - - // Display a 'pretty' label or the path in the UI. - if (folderListPref.value == 2) { - // Custom path selected and is configured - downloadFolder.label = this._getDisplayNameOfFile(currentDirPref.value); - iconUrlSpec = fph.getURLSpecFromFile(currentDirPref.value); - } else if (folderListPref.value == 1) { - // 'Downloads' - // In 1.5, this pointed to a folder we created called 'My Downloads' - // and was available as an option in the 1.5 drop down. On XP this - // was in My Documents, on OSX it was in User Docs. In 2.0, we did - // away with the drop down option, although the special label was - // still supported for the folder if it existed. Because it was - // not exposed it was rarely used. - // With 3.0, a new desktop folder - 'Downloads' was introduced for - // platforms and versions that don't support a default system downloads - // folder. See nsDownloadManager for details. - downloadFolder.label = bundlePreferences.getString("downloadsFolderName"); - iconUrlSpec = fph.getURLSpecFromFile(this._indexToFolder(1)); - } else { - // 'Desktop' - downloadFolder.label = bundlePreferences.getString("desktopFolderName"); - iconUrlSpec = fph.getURLSpecFromFile(this._getDownloadsFolder("Desktop")); - } - downloadFolder.image = "moz-icon://" + iconUrlSpec + "?size=16"; - - // don't override the preference's value in UI - return undefined; - }, - - /** - * Returns the textual path of a folder in readable form. - */ - _getDisplayNameOfFile: function (aFolder) - { - // TODO: would like to add support for 'Downloads on Macintosh HD' - // for OS X users. - return aFolder ? aFolder.path : ""; - }, - - /** - * Returns the Downloads folder. If aFolder is "Desktop", then the Downloads - * folder returned is the desktop folder; otherwise, it is a folder whose name - * indicates that it is a download folder and whose path is as determined by - * the XPCOM directory service via the download manager's attribute - * defaultDownloadsDirectory. - * - * @throws if aFolder is not "Desktop" or "Downloads" - */ - _getDownloadsFolder: function (aFolder) - { - switch (aFolder) { - case "Desktop": - var fileLoc = Components.classes["@mozilla.org/file/directory_service;1"] - .getService(Components.interfaces.nsIProperties); - return fileLoc.get("Desk", Components.interfaces.nsILocalFile); - break; - case "Downloads": - var dnldMgr = Components.classes["@mozilla.org/download-manager;1"] - .getService(Components.interfaces.nsIDownloadManager); - return dnldMgr.defaultDownloadsDirectory; - break; - } - throw "ASSERTION FAILED: folder type should be 'Desktop' or 'Downloads'"; - }, - - /** - * Determines the type of the given folder. - * - * @param aFolder - * the folder whose type is to be determined - * @returns integer - * 0 if aFolder is the Desktop or is unspecified, - * 1 if aFolder is the Downloads folder, - * 2 otherwise - */ - _folderToIndex: function (aFolder) - { - if (!aFolder || aFolder.equals(this._getDownloadsFolder("Desktop"))) - return 0; - else if (aFolder.equals(this._getDownloadsFolder("Downloads"))) - return 1; - return 2; - }, - - /** - * Converts an integer into the corresponding folder. - * - * @param aIndex - * an integer - * @returns the Desktop folder if aIndex == 0, - * the Downloads folder if aIndex == 1, - * the folder stored in browser.download.dir - */ - _indexToFolder: function (aIndex) - { - switch (aIndex) { - case 0: - return this._getDownloadsFolder("Desktop"); - case 1: - return this._getDownloadsFolder("Downloads"); - } - var currentDirPref = document.getElementById("browser.download.dir"); - return currentDirPref.value; - }, - - /** - * Returns the value for the browser.download.folderList preference. - */ - getFolderListPref: function () - { - var folderListPref = document.getElementById("browser.download.folderList"); - switch (folderListPref.value) { - case 0: // Desktop - case 1: // Downloads - return folderListPref.value; - break; - case 2: // Custom - var currentDirPref = document.getElementById("browser.download.dir"); - if (currentDirPref.value) { - // Resolve to a known location if possible. We are writing out - // to prefs on this call, so now would be a good time to do it. - return this._folderToIndex(currentDirPref.value); - } - return 0; - break; - } - }, - - /** - * Hide/show the "Show my windows and tabs from last time" option based - * on the value of the browser.privatebrowsing.autostart pref. - */ - updateBrowserStartupLastSession: function() - { - let pbAutoStartPref = document.getElementById("browser.privatebrowsing.autostart"); - let startupPref = document.getElementById("browser.startup.page"); - let menu = document.getElementById("browserStartupPage"); - let option = document.getElementById("browserStartupLastSession"); - if (pbAutoStartPref.value) { - option.setAttribute("disabled", "true"); - if (option.selected) { - menu.selectedItem = document.getElementById("browserStartupHomePage"); - } - } else { - option.removeAttribute("disabled"); - startupPref.updateElements(); // select the correct index in the startup menulist - } - } -}; diff --git a/components/preferences/main.xul b/components/preferences/main.xul deleted file mode 100644 index bb51947..0000000 --- a/components/preferences/main.xul +++ /dev/null @@ -1,188 +0,0 @@ -<?xml version="1.0"?> - -# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -<!DOCTYPE overlay [ - <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> - <!ENTITY % mainDTD SYSTEM "chrome://browser/locale/preferences/main.dtd"> - <!ENTITY % aboutHomeDTD SYSTEM "chrome://browser/locale/aboutHome.dtd"> - %brandDTD; - %mainDTD; - %aboutHomeDTD; -]> - -<overlay id="MainPaneOverlay" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - - <prefpane id="paneMain" - onpaneload="gMainPane.init();" - helpTopic="prefs-main"> - - <script type="application/javascript" src="chrome://browser/content/preferences/main.js"/> - - <preferences id="mainPreferences"> - <!-- XXX Button preferences --> - - <!-- Startup --> - <preference id="browser.startup.page" - name="browser.startup.page" - type="int"/> - <preference id="browser.startup.homepage" - name="browser.startup.homepage" - type="wstring"/> - - <preference id="pref.browser.homepage.disable_button.current_page" - name="pref.browser.homepage.disable_button.current_page" - type="bool"/> - <preference id="pref.browser.homepage.disable_button.bookmark_page" - name="pref.browser.homepage.disable_button.bookmark_page" - type="bool"/> - <preference id="pref.browser.homepage.disable_button.restore_default" - name="pref.browser.homepage.disable_button.restore_default" - type="bool"/> - - <preference id="browser.privatebrowsing.autostart" - name="browser.privatebrowsing.autostart" - type="bool" - onchange="gMainPane.updateBrowserStartupLastSession();"/> - - <!-- Downloads --> - <preference id="browser.download.manager.showWhenStarting" - name="browser.download.manager.showWhenStarting" - type="bool" - onchange="gMainPane.showDownloadsWhenStartingPrefChanged();"/> - <preference id="browser.download.manager.closeWhenDone" - name="browser.download.manager.closeWhenDone" - type="bool"/> - <preference id="browser.download.useDownloadDir" - name="browser.download.useDownloadDir" - type="bool"/> - <preference id="browser.download.dir" - name="browser.download.dir" - type="file" - onchange="gMainPane.displayDownloadDirPref();"/> - <preference id="browser.download.folderList" name="browser.download.folderList" type="int"/> - <preference id="browser.download.useToolkitUI" name="browser.download.useToolkitUI" type="bool" /> -#ifdef XP_WIN - <preference id="browser.download.saveZoneInformation" name="browser.download.saveZoneInformation" type="int" /> -#endif - - </preferences> - - <stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/> - - <!-- Startup --> - <groupbox id="startupGroup"> - <caption label="&startup.label;"/> - - <hbox align="center"> - <label value="&startupPage.label;" accesskey="&startupPage.accesskey;" - control="browserStartupPage"/> - <menulist id="browserStartupPage" preference="browser.startup.page"> - <menupopup> - <menuitem label="&startupHomePage.label;" value="1" id="browserStartupHomePage"/> - <menuitem label="&startupBlankPage.label;" value="0" id="browserStartupBlank"/> - <menuitem label="&startupLastSession.label;" value="3" id="browserStartupLastSession"/> - </menupopup> - </menulist> - </hbox> - <separator class="thin"/> - <hbox align="center"> - <label value="&homepage.label;" accesskey="&homepage.accesskey;" control="browserHomePage"/> - <textbox id="browserHomePage" class="padded uri-element" flex="1" - type="autocomplete" autocompletesearch="history" - onsyncfrompreference="return gMainPane.syncFromHomePref();" - onsynctopreference="return gMainPane.syncToHomePref(this.value);" - oninput="gNewtabUrl.writeNewtabUrl(null, this.value);" - placeholder="&abouthome.pageTitle;" - preference="browser.startup.homepage"/> - </hbox> - <hbox align="center" pack="end"> - <button label="" accesskey="&useCurrentPage.accesskey;" - label1="&useCurrentPage.label;" - label2="&useMultiple.label;" - oncommand="gMainPane.setHomePageToCurrent(); gNewtabUrl.writeNewtabUrl();" - id="useCurrent" - preference="pref.browser.homepage.disable_button.current_page"/> - <button label="&chooseBookmark.label;" accesskey="&chooseBookmark.accesskey;" - oncommand="gMainPane.setHomePageToBookmark(); gNewtabUrl.writeNewtabUrl();" - id="useBookmark" - preference="pref.browser.homepage.disable_button.bookmark_page"/> - <button label="&restoreDefault.label;" accesskey="&restoreDefault.accesskey;" - oncommand="gMainPane.restoreDefaultHomePage(); gNewtabUrl.writeNewtabUrl();" - id="restoreDefaultHomePage" - preference="pref.browser.homepage.disable_button.restore_default"/> - </hbox> - </groupbox> - - <!-- Downloads --> - <groupbox id="downloadsGroup"> - <caption label="&downloads.label;"/> - - <checkbox id="showWhenDownloading" label="&showWhenDownloading.label;" - accesskey="&showWhenDownloading.accesskey;" - preference="browser.download.manager.showWhenStarting" - onsyncfrompreference="return gMainPane.readShowDownloadsWhenStarting();"/> - <checkbox id="closeWhenDone" label="&closeWhenDone.label;" - accesskey="&closeWhenDone.accesskey;" class="indent" - preference="browser.download.manager.closeWhenDone"/> - - <separator class="thin"/> - - <radiogroup id="saveWhere" - preference="browser.download.useDownloadDir" - onsyncfrompreference="return gMainPane.readUseDownloadDir();"> - <hbox id="saveToRow"> - <radio id="saveTo" value="true" - label="&saveTo.label;" - accesskey="&saveTo.accesskey;" - aria-labelledby="saveTo downloadFolder"/> - <filefield id="downloadFolder" flex="1" - preference="browser.download.folderList" - preference-editable="true" - aria-labelledby="saveTo" - onsyncfrompreference="return gMainPane.displayDownloadDirPref();" - onsynctopreference="return gMainPane.getFolderListPref()"/> - <button id="chooseFolder" oncommand="gMainPane.chooseFolder();" -#ifdef XP_MACOSX - accesskey="&chooseFolderMac.accesskey;" - label="&chooseFolderMac.label;" -#else - accesskey="&chooseFolderWin.accesskey;" - label="&chooseFolderWin.label;" -#endif - preference="browser.download.folderList" - onsynctopreference="return gMainPane.getFolderListPref();"/> - </hbox> - <radio id="alwaysAsk" value="false" - label="&alwaysAsk.label;" - accesskey="&alwaysAsk.accesskey;"/> - </radiogroup> -#if 0 -<!-- Disabled for now -- ToolkitUI DM is nonfunctional. --> - <checkbox id="classicDownloadWindow" - preference="browser.download.useToolkitUI" - label="&toolkit.classic.download.window.label;" /> -#endif -#ifdef XP_WIN - <hbox align="center"> - <label id="zoneInfoLabel" control="zoneInfo-menu">&zoneInfo.label;</label> - <menulist id="zoneInfo-menu" - preference="browser.download.saveZoneInformation" - sizetopopup="always"> - <menupopup> - <menuitem label="&zoneInfo.never;" value="0" /> - <menuitem label="&zoneInfo.always;" value="1" /> - <menuitem label="&zoneInfo.system;" value="2" /> - </menupopup> - </menulist> - </hbox> -#endif - </groupbox> - - </prefpane> - -</overlay> diff --git a/components/preferences/moz.build b/components/preferences/moz.build deleted file mode 100644 index 31fddae..0000000 --- a/components/preferences/moz.build +++ /dev/null @@ -1,14 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - - -for var in ('MOZ_APP_NAME', 'MOZ_MACBUNDLE_NAME'): - DEFINES[var] = CONFIG[var] - -if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk2', 'gtk3', 'cocoa'): - DEFINES['HAVE_SHELL_SERVICE'] = 1 - -JAR_MANIFESTS += ['jar.mn']
\ No newline at end of file diff --git a/components/preferences/newtaburl.js b/components/preferences/newtaburl.js deleted file mode 100644 index 3c82df8..0000000 --- a/components/preferences/newtaburl.js +++ /dev/null @@ -1,102 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var gNewtabUrl = {
- /**
- * Writes browser.newtab.url with the appropriate value.
- * If the choice is "my home page", get and sanitize
- * the browser home page URL to make it suitable for newtab use.
- *
- * Called from prefwindow ondialogaccept in preferences.xul,
- * newtabPage oncommand in tabs.xul, browserHomePage oninput,
- * useCurrent, useBookmark and restoreDefaultHomePage oncommand
- * in main.xul to consider instantApply.
- */
- writeNewtabUrl: function(newtabUrlChoice, browserHomepageUrl) {
- try {
- if (newtabUrlChoice) {
- if (Services.prefs.getBoolPref("browser.preferences.instantApply")) {
- newtabUrlChoice = parseInt(newtabUrlChoice);
- } else {
- return;
- }
- } else {
- if (this.newtabUrlChoiceIsSet) {
- newtabUrlChoice = Services.prefs.getIntPref("browser.newtab.choice");
- } else {
- newtabUrlChoice = this.getNewtabChoice();
- }
- }
- if (browserHomepageUrl || browserHomepageUrl == "") {
- if (Services.prefs.getBoolPref("browser.preferences.instantApply")) {
- if (browserHomepageUrl == "") {
- browserHomepageUrl = "about:home";
- }
- } else {
- return;
- }
- } else {
- browserHomepageUrl = Services.prefs.getComplexValue("browser.startup.homepage",
- Components.interfaces.nsIPrefLocalizedString).data;
- }
- let newtabUrlPref = Services.prefs.getCharPref("browser.newtab.url");
- switch (newtabUrlChoice) {
- case 1:
- newtabUrlPref = "about:logopage";
- break;
- case 2:
- newtabUrlPref = Services.prefs.getDefaultBranch("browser.")
- .getComplexValue("startup.homepage",
- Components.interfaces.nsIPrefLocalizedString).data;
- break;
- case 3:
- // If url is a pipe-delimited set of pages, just take the first one.
- let newtabUrlSanitizedPref=browserHomepageUrl.split("|")[0];
- // XXX: do we need extra sanitation here, e.g. for invalid URLs?
- Services.prefs.setCharPref("browser.newtab.myhome", newtabUrlSanitizedPref);
- newtabUrlPref = newtabUrlSanitizedPref;
- break;
- case 4:
- newtabUrlPref = "about:newtab";
- break;
- default:
- // In case of any other value it's a custom URL, consider instantApply.
- if (this.newtabPageCustom) {
- newtabUrlPref = this.newtabPageCustom;
- }
- }
- Services.prefs.setCharPref("browser.newtab.url",newtabUrlPref);
- } catch(e) { console.error(e); }
- },
-
- /**
- * Determines the value of browser.newtab.choice based
- * on the value of browser.newtab.url
- *
- * @returns the value of browser.newtab.choice
- */
- getNewtabChoice: function() {
- let newtabUrlPref = Services.prefs.getCharPref("browser.newtab.url");
- let browserHomepageUrl = Services.prefs.getComplexValue("browser.startup.homepage",
- Components.interfaces.nsIPrefLocalizedString).data;
- let newtabUrlSanitizedPref = browserHomepageUrl.split("|")[0];
- let defaultStartupHomepage = Services.prefs.getDefaultBranch("browser.")
- .getComplexValue("startup.homepage",
- Components.interfaces.nsIPrefLocalizedString).data;
- switch (newtabUrlPref) {
- case "about:logopage":
- return 1;
- case defaultStartupHomepage:
- return 2;
- case newtabUrlSanitizedPref:
- return 3;
- case "about:newtab":
- return 4;
- default: // Custom URL entered.
- // We need this to consider instantApply.
- this.newtabPageCustom = newtabUrlPref;
- return 0;
- }
- }
-};
diff --git a/components/preferences/permissions.js b/components/preferences/permissions.js deleted file mode 100644 index 4b1bf41..0000000 --- a/components/preferences/permissions.js +++ /dev/null @@ -1,463 +0,0 @@ -/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -Components.utils.import("resource://gre/modules/Services.jsm"); - -const nsIPermissionManager = Components.interfaces.nsIPermissionManager; -const nsICookiePermission = Components.interfaces.nsICookiePermission; - -const NOTIFICATION_FLUSH_PERMISSIONS = "flush-pending-permissions"; - -function Permission(principal, type, capability) -{ - this.principal = principal; - this.origin = principal.origin; - this.type = type; - this.capability = capability; -} - -var gPermissionManager = { - _type : "", - _permissions : [], - _permissionsToAdd : new Map(), - _permissionsToDelete : new Map(), - _bundle : null, - _tree : null, - _observerRemoved : false, - - _view: { - _rowCount: 0, - get rowCount() - { - return this._rowCount; - }, - getCellText: function (aRow, aColumn) - { - if (aColumn.id == "siteCol") - return gPermissionManager._permissions[aRow].origin; - else if (aColumn.id == "statusCol") - return gPermissionManager._permissions[aRow].capability; - return ""; - }, - - isSeparator: function(aIndex) { return false; }, - isSorted: function() { return false; }, - isContainer: function(aIndex) { return false; }, - setTree: function(aTree){}, - getImageSrc: function(aRow, aColumn) {}, - getProgressMode: function(aRow, aColumn) {}, - getCellValue: function(aRow, aColumn) {}, - cycleHeader: function(column) {}, - getRowProperties: function(row){ return ""; }, - getColumnProperties: function(column){ return ""; }, - getCellProperties: function(row,column){ - if (column.element.getAttribute("id") == "siteCol") - return "ltr"; - - return ""; - } - }, - - _getCapabilityString: function (aCapability) - { - var stringKey = null; - switch (aCapability) { - case nsIPermissionManager.ALLOW_ACTION: - stringKey = "can"; - break; - case nsIPermissionManager.DENY_ACTION: - stringKey = "cannot"; - break; - case nsICookiePermission.ACCESS_ALLOW_FIRST_PARTY_ONLY: - stringKey = "canAccessFirstParty"; - break; - case nsICookiePermission.ACCESS_SESSION: - stringKey = "canSession"; - break; - } - return this._bundle.getString(stringKey); - }, - - addPermission: function (aCapability) - { - var textbox = document.getElementById("url"); - var input_url = textbox.value.replace(/^\s*/, ""); // trim any leading space - let principal; - try { - // The origin accessor on the principal object will throw if the - // principal doesn't have a canonical origin representation. This will - // help catch cases where the URI parser parsed something like - // `localhost:8080` as having the scheme `localhost`, rather than being - // an invalid URI. A canonical origin representation is required by the - // permission manager for storage, so this won't prevent any valid - // permissions from being entered by the user. - let uri; - try { - uri = Services.io.newURI(input_url, null, null); - principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri); - // If we have ended up with an unknown scheme, the following will throw. - principal.origin; - } catch(ex) { - uri = Services.io.newURI("http://" + input_url, null, null); - principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri); - // If we have ended up with an unknown scheme, the following will throw. - principal.origin; - } - } catch(ex) { - var message = this._bundle.getString("invalidURI"); - var title = this._bundle.getString("invalidURITitle"); - Services.prompt.alert(window, title, message); - return; - } - - var capabilityString = this._getCapabilityString(aCapability); - - // check whether the permission already exists, if not, add it - let permissionExists = false; - let capabilityExists = false; - for (var i = 0; i < this._permissions.length; ++i) { - if (this._permissions[i].principal.equals(principal)) { - permissionExists = true; - capabilityExists = this._permissions[i].capability == capabilityString; - if (!capabilityExists) { - this._permissions[i].capability = capabilityString; - } - break; - } - } - - - let permissionParams = {principal: principal, type: this._type, capability: aCapability}; - if (!permissionExists) { - this._permissionsToAdd.set(principal.origin, permissionParams); - this._addPermission(permissionParams); - } - else if (!capabilityExists) { - this._permissionsToAdd.set(principal.origin, permissionParams); - this._handleCapabilityChange(); - } - - textbox.value = ""; - textbox.focus(); - - // covers a case where the site exists already, so the buttons don't disable - this.onHostInput(textbox); - - // enable "remove all" button as needed - document.getElementById("removeAllPermissions").disabled = this._permissions.length == 0; - }, - - _removePermission: function(aPermission) - { - this._removePermissionFromList(aPermission.principal); - - // If this permission was added during this session, let's remove - // it from the pending adds list to prevent calls to the - // permission manager. - let isNewPermission = this._permissionsToAdd.delete(aPermission.principal.origin); - - if (!isNewPermission) { - this._permissionsToDelete.set(aPermission.principal.origin, aPermission); - } - - }, - - _handleCapabilityChange: function () - { - // Re-do the sort, if the status changed from Block to Allow - // or vice versa, since if we're sorted on status, we may no - // longer be in order. - if (this._lastPermissionSortColumn == "statusCol") { - this._resortPermissions(); - } - this._tree.treeBoxObject.invalidate(); - }, - - _addPermission: function(aPermission) - { - this._addPermissionToList(aPermission); - ++this._view._rowCount; - this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, 1); - // Re-do the sort, since we inserted this new item at the end. - this._resortPermissions(); - }, - - _resortPermissions: function() - { - gTreeUtils.sort(this._tree, this._view, this._permissions, - this._lastPermissionSortColumn, - this._permissionsComparator, - this._lastPermissionSortColumn, - !this._lastPermissionSortAscending); // keep sort direction - }, - - onHostInput: function (aSiteField) - { - document.getElementById("btnSession").disabled = !aSiteField.value; - document.getElementById("btnBlock").disabled = !aSiteField.value; - document.getElementById("btnAllow").disabled = !aSiteField.value; - }, - - onWindowKeyPress: function (aEvent) - { - if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE) - window.close(); - }, - - onHostKeyPress: function (aEvent) - { - if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN) - document.getElementById("btnAllow").click(); - }, - - onLoad: function () - { - this._bundle = document.getElementById("bundlePreferences"); - var params = window.arguments[0]; - this.init(params); - }, - - init: function (aParams) - { - if (this._type) { - // reusing an open dialog, clear the old observer - this.uninit(); - } - - this._type = aParams.permissionType; - this._manageCapability = aParams.manageCapability; - - var permissionsText = document.getElementById("permissionsText"); - while (permissionsText.hasChildNodes()) - permissionsText.removeChild(permissionsText.firstChild); - permissionsText.appendChild(document.createTextNode(aParams.introText)); - - document.title = aParams.windowTitle; - - document.getElementById("btnBlock").hidden = !aParams.blockVisible; - document.getElementById("btnSession").hidden = !aParams.sessionVisible; - document.getElementById("btnAllow").hidden = !aParams.allowVisible; - - var urlFieldVisible = (aParams.blockVisible || aParams.sessionVisible || aParams.allowVisible); - - var urlField = document.getElementById("url"); - urlField.value = aParams.prefilledHost; - urlField.hidden = !urlFieldVisible; - - this.onHostInput(urlField); - - var urlLabel = document.getElementById("urlLabel"); - urlLabel.hidden = !urlFieldVisible; - - let treecols = document.getElementsByTagName("treecols")[0]; - treecols.addEventListener("click", event => { - if (event.target.nodeName != "treecol" || event.button != 0) { - return; - } - - let sortField = event.target.getAttribute("data-field-name"); - if (!sortField) { - return; - } - - gPermissionManager.onPermissionSort(sortField); - }); - - Services.obs.notifyObservers(null, NOTIFICATION_FLUSH_PERMISSIONS, this._type); - Services.obs.addObserver(this, "perm-changed", false); - - this._loadPermissions(); - - urlField.focus(); - }, - - uninit: function () - { - if (!this._observerRemoved) { - Services.obs.removeObserver(this, "perm-changed"); - - this._observerRemoved = true; - } - }, - - observe: function (aSubject, aTopic, aData) - { - if (aTopic == "perm-changed") { - var permission = aSubject.QueryInterface(Components.interfaces.nsIPermission); - - // Ignore unrelated permission types. - if (permission.type != this._type) - return; - - if (aData == "added") { - this._addPermission(permission); - } - else if (aData == "changed") { - for (var i = 0; i < this._permissions.length; ++i) { - if (permission.matches(this._permissions[i].principal, true)) { - this._permissions[i].capability = this._getCapabilityString(permission.capability); - break; - } - } - this._handleCapabilityChange(); - } - else if (aData == "deleted") { - this._removePermissionFromList(permission.principal); - } - } - }, - - onPermissionSelected: function () - { - var hasSelection = this._tree.view.selection.count > 0; - var hasRows = this._tree.view.rowCount > 0; - document.getElementById("removePermission").disabled = !hasRows || !hasSelection; - document.getElementById("removeAllPermissions").disabled = !hasRows; - }, - - onPermissionDeleted: function () - { - if (!this._view.rowCount) - return; - var removedPermissions = []; - gTreeUtils.deleteSelectedItems(this._tree, this._view, this._permissions, removedPermissions); - for (var i = 0; i < removedPermissions.length; ++i) { - var p = removedPermissions[i]; - this._removePermission(p); - } - document.getElementById("removePermission").disabled = !this._permissions.length; - document.getElementById("removeAllPermissions").disabled = !this._permissions.length; - }, - - onAllPermissionsDeleted: function () - { - if (!this._view.rowCount) - return; - var removedPermissions = []; - gTreeUtils.deleteAll(this._tree, this._view, this._permissions, removedPermissions); - for (var i = 0; i < removedPermissions.length; ++i) { - var p = removedPermissions[i]; - this._removePermission(p); - } - document.getElementById("removePermission").disabled = true; - document.getElementById("removeAllPermissions").disabled = true; - }, - - onPermissionKeyPress: function (aEvent) - { - if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE -#ifdef XP_MACOSX - || aEvent.keyCode == KeyEvent.DOM_VK_BACK_SPACE -#endif - ) { - this.onPermissionDeleted(); - } - }, - - _lastPermissionSortColumn: "", - _lastPermissionSortAscending: false, - _permissionsComparator : function (a, b) - { - return a.toLowerCase().localeCompare(b.toLowerCase()); - }, - - - onPermissionSort: function (aColumn) - { - this._lastPermissionSortAscending = gTreeUtils.sort(this._tree, - this._view, - this._permissions, - aColumn, - this._permissionsComparator, - this._lastPermissionSortColumn, - this._lastPermissionSortAscending); - this._lastPermissionSortColumn = aColumn; - }, - - onApplyChanges: function() - { - // Stop observing permission changes since we are about - // to write out the pending adds/deletes and don't need - // to update the UI - this.uninit(); - - for (let permissionParams of this._permissionsToAdd.values()) { - Services.perms.addFromPrincipal(permissionParams.principal, permissionParams.type, permissionParams.capability); - } - - for (let p of this._permissionsToDelete.values()) { - Services.perms.removeFromPrincipal(p.principal, p.type); - } - - window.close(); - }, - - _loadPermissions: function () - { - this._tree = document.getElementById("permissionsTree"); - this._permissions = []; - - // load permissions into a table - var count = 0; - var enumerator = Services.perms.enumerator; - while (enumerator.hasMoreElements()) { - var nextPermission = enumerator.getNext().QueryInterface(Components.interfaces.nsIPermission); - this._addPermissionToList(nextPermission); - } - - this._view._rowCount = this._permissions.length; - - // sort and display the table - this._tree.view = this._view; - this.onPermissionSort("origin"); - - // disable "remove all" button if there are none - document.getElementById("removeAllPermissions").disabled = this._permissions.length == 0; - }, - - _addPermissionToList: function (aPermission) - { - if (aPermission.type == this._type && - (!this._manageCapability || - (aPermission.capability == this._manageCapability))) { - - var principal = aPermission.principal; - var capabilityString = this._getCapabilityString(aPermission.capability); - var p = new Permission(principal, - aPermission.type, - capabilityString); - this._permissions.push(p); - } - }, - - _removePermissionFromList: function (aPrincipal) - { - for (let i = 0; i < this._permissions.length; ++i) { - if (this._permissions[i].principal.equals(aPrincipal)) { - this._permissions.splice(i, 1); - this._view._rowCount--; - this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, -1); - this._tree.treeBoxObject.invalidate(); - break; - } - } - }, - - setOrigin: function (aOrigin) - { - document.getElementById("url").value = aOrigin; - } -}; - -function setOrigin(aOrigin) -{ - gPermissionManager.setOrigin(aOrigin); -} - -function initWithParams(aParams) -{ - gPermissionManager.init(aParams); -} - diff --git a/components/preferences/permissions.xul b/components/preferences/permissions.xul deleted file mode 100644 index 33806cc..0000000 --- a/components/preferences/permissions.xul +++ /dev/null @@ -1,85 +0,0 @@ -<?xml version="1.0"?> - -<!-- -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- --> -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> -<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?> - -<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/permissions.dtd" > - -<window id="PermissionsDialog" class="windowDialog" - windowtype="Browser:Permissions" - title="&window.title;" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - style="width: &window.width;;" - onload="gPermissionManager.onLoad();" - onunload="gPermissionManager.uninit();" - persist="screenX screenY width height" - onkeypress="gPermissionManager.onWindowKeyPress(event);"> - - <script src="chrome://global/content/treeUtils.js"/> - <script src="chrome://browser/content/preferences/permissions.js"/> - - <stringbundle id="bundlePreferences" - src="chrome://browser/locale/preferences/preferences.properties"/> - - <keyset> - <key key="&windowClose.key;" modifiers="accel" oncommand="window.close();"/> - </keyset> - - <vbox class="contentPane" flex="1"> - <description id="permissionsText" control="url"/> - <separator class="thin"/> - <label id="urlLabel" control="url" value="&address.label;" accesskey="&address.accesskey;"/> - <hbox align="start"> - <textbox id="url" flex="1" - oninput="gPermissionManager.onHostInput(event.target);" - onkeypress="gPermissionManager.onHostKeyPress(event);"/> - </hbox> - <hbox pack="end"> - <button id="btnBlock" disabled="true" label="&block.label;" accesskey="&block.accesskey;" - oncommand="gPermissionManager.addPermission(nsIPermissionManager.DENY_ACTION);"/> - <button id="btnSession" disabled="true" label="&session.label;" accesskey="&session.accesskey;" - oncommand="gPermissionManager.addPermission(nsICookiePermission.ACCESS_SESSION);"/> - <button id="btnAllow" disabled="true" label="&allow.label;" default="true" accesskey="&allow.accesskey;" - oncommand="gPermissionManager.addPermission(nsIPermissionManager.ALLOW_ACTION);"/> - </hbox> - <separator class="thin"/> - <tree id="permissionsTree" flex="1" style="height: 18em;" - hidecolumnpicker="true" - onkeypress="gPermissionManager.onPermissionKeyPress(event)" - onselect="gPermissionManager.onPermissionSelected();"> - <treecols> - <treecol id="siteCol" label="&treehead.sitename.label;" flex="3" - data-field-name="origin" persist="width"/> - <splitter class="tree-splitter"/> - <treecol id="statusCol" label="&treehead.status.label;" flex="1" - data-field-name="capability" persist="width"/> - </treecols> - <treechildren/> - </tree> - </vbox> - <vbox> - <hbox class="actionButtons" align="left" flex="1"> - <button id="removePermission" disabled="true" - accesskey="&removepermission.accesskey;" - icon="remove" label="&removepermission.label;" - oncommand="gPermissionManager.onPermissionDeleted();"/> - <button id="removeAllPermissions" - icon="clear" label="&removeallpermissions.label;" - accesskey="&removeallpermissions.accesskey;" - oncommand="gPermissionManager.onAllPermissionsDeleted();"/> - </hbox> - <spacer flex="1"/> - <hbox class="actionButtons" align="right" flex="1"> - <button oncommand="close();" icon="close" - label="&button.cancel.label;" accesskey="&button.cancel.accesskey;" /> - <button id="btnApplyChanges" oncommand="gPermissionManager.onApplyChanges();" icon="save" - label="&button.ok.label;" accesskey="&button.ok.accesskey;"/> - </hbox> - <resizer type="window" dir="bottomend"/> - </vbox> -</window> diff --git a/components/preferences/preferences.xul b/components/preferences/preferences.xul deleted file mode 100644 index a1d9c8c..0000000 --- a/components/preferences/preferences.xul +++ /dev/null @@ -1,92 +0,0 @@ -<?xml version="1.0"?> - -# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -<?xml-stylesheet href="chrome://global/skin/global.css"?> -<?xml-stylesheet href="chrome://mozapps/content/preferences/preferences.css"?> -<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?> - -<!-- XXX This should be in applications.xul, but bug 393953 means putting it - - there causes the Applications pane not to work the first time you open - - the Preferences dialog in a browsing session, so we work around the problem - - by putting it here instead. - --> -<?xml-stylesheet href="chrome://browser/content/preferences/handlers.css"?> -<?xml-stylesheet href="chrome://browser/skin/preferences/applications.css"?> - -<!DOCTYPE prefwindow [ -<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> -<!ENTITY % preferencesDTD SYSTEM "chrome://browser/locale/preferences/preferences.dtd"> -%brandDTD; -%preferencesDTD; -]> - -#ifdef XP_WIN -#define USE_WIN_TITLE_STYLE -#endif - -#ifdef XP_MACOSX -<?xul-overlay href="chrome://browser/content/macBrowserOverlay.xul"?> -#endif - -<prefwindow type="prefwindow" - id="BrowserPreferences" - windowtype="Browser:Preferences" - ondialoghelp="openPrefsHelp()" -#ifdef USE_WIN_TITLE_STYLE - title="&prefWindow.titleWin;" -#else -#ifdef XP_UNIX -#ifndef XP_MACOSX - title="&prefWindow.titleGNOME;" -#endif -#endif -#endif - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" -#ifdef USE_WIN_TITLE_STYLE - style="&prefWinMinSize.styleWin2;" -#else -#ifdef XP_MACOSX - style="&prefWinMinSize.styleMac;" -#else - style="&prefWinMinSize.styleGNOME;" -#endif -#endif - onunload="if (typeof gSecurityPane != 'undefined') gSecurityPane.syncAddonSecurityLevel();" - ondialogaccept="gNewtabUrl.writeNewtabUrl();"> - - <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/> - <script type="application/javascript" src="chrome://browser/content/preferences/newtaburl.js"/> - - <stringbundle id="bundleBrand" src="chrome://branding/locale/brand.properties"/> - <stringbundle id="bundlePreferences" - src="chrome://browser/locale/preferences/preferences.properties"/> - - <prefpane id="paneMain" label="&paneGeneral.title;" - src="chrome://browser/content/preferences/main.xul"/> - <prefpane id="paneTabs" label="&paneTabs.title;" - src="chrome://browser/content/preferences/tabs.xul"/> - <prefpane id="paneContent" label="&paneContent.title;" - src="chrome://browser/content/preferences/content.xul"/> - <prefpane id="paneApplications" label="&paneApplications.title;" - src="chrome://browser/content/preferences/applications.xul"/> - <prefpane id="panePrivacy" label="&panePrivacy.title;" - src="chrome://browser/content/preferences/privacy.xul"/> - <prefpane id="paneSecurity" label="&paneSecurity.title;" - src="chrome://browser/content/preferences/security.xul"/> -#ifdef MOZ_SERVICES_SYNC - <prefpane id="paneSync" label="&paneSync.title;" - src="chrome://browser/content/preferences/sync.xul"/> -#endif - <prefpane id="paneAdvanced" label="&paneAdvanced.title;" - src="chrome://browser/content/preferences/advanced.xul"/> - -#ifdef XP_MACOSX -#include ../../base/content/browserMountPoints.inc -#endif - -</prefwindow> - diff --git a/components/preferences/privacy.js b/components/preferences/privacy.js deleted file mode 100644 index e2a871a..0000000 --- a/components/preferences/privacy.js +++ /dev/null @@ -1,485 +0,0 @@ -/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); - -var gPrivacyPane = { - - /** - * Whether the use has selected the auto-start private browsing mode in the UI. - */ - _autoStartPrivateBrowsing: false, - - /** - * Whether the prompt to restart Firefox should appear when changing the autostart pref. - */ - _shouldPromptForRestart: true, - - /** - * Sets up the UI for the number of days of history to keep, and updates the - * label of the "Clear Now..." button. - * Also restores the previously selected tab or tab index passed as argument. - */ - init: function () - { - this._inited = true; - var privacyPrefs = document.getElementById("privacyPrefs"); - - var extraArgs = window.arguments[1]; - if (extraArgs && extraArgs["privacyTab"]){ - privacyPrefs.selectedTab = document.getElementById(extraArgs["privacyTab"]); - } else { - var preference = document.getElementById("browser.preferences.privacy.selectedTabIndex"); - if (preference.value !== null) - privacyPrefs.selectedIndex = preference.value; - } - - this._updateSanitizeSettingsButton(); - this.initializeHistoryMode(); - this.updateHistoryModePane(); - this.updatePrivacyMicroControls(); - this.initAutoStartPrivateBrowsingReverter(); - }, - - /** - * Stores the identity of the current tab in preferences so that the selected - * tab can be persisted between openings of the preferences window. - */ - tabSelectionChanged: function () - { - if (!this._inited) - return; - var privacyPrefs = document.getElementById("privacyPrefs"); - var preference = document.getElementById("browser.preferences.privacy.selectedTabIndex"); - preference.valueFromPreferences = privacyPrefs.selectedIndex; - }, - - // HISTORY MODE - - /** - * The list of preferences which affect the initial history mode settings. - * If the auto start private browsing mode pref is active, the initial - * history mode would be set to "Don't remember anything". - * If all of these preferences have their default values, and the auto-start - * private browsing mode is not active, the initial history mode would be - * set to "Remember everything". - * Otherwise, the initial history mode would be set to "Custom". - * - * Extensions adding their own preferences can append their IDs to this array if needed. - */ - prefsForDefault: [ - "places.history.enabled", - "browser.formfill.enable", - "network.cookie.cookieBehavior", - "network.cookie.lifetimePolicy", - "privacy.sanitize.sanitizeOnShutdown" - ], - - /** - * The list of control IDs which are dependent on the auto-start private - * browsing setting, such that in "Custom" mode they would be disabled if - * the auto-start private browsing checkbox is checked, and enabled otherwise. - * - * Extensions adding their own controls can append their IDs to this array if needed. - */ - dependentControls: [ - "rememberHistory", - "rememberForms", - "keepUntil", - "keepCookiesUntil", - "alwaysClear", - "clearDataSettings" - ], - - /** - * Check whether all the preferences values are set to their default values - * - * @param aPrefs an array of pref names to check for - * @returns boolean true if all of the prefs are set to their default values, - * false otherwise - */ - _checkDefaultValues: function(aPrefs) { - for (let i = 0; i < aPrefs.length; ++i) { - let pref = document.getElementById(aPrefs[i]); - if (pref.value != pref.defaultValue) - return false; - } - return true; - }, - - /** - * Initialize the history mode menulist based on the privacy preferences - */ - initializeHistoryMode: function PPP_initializeHistoryMode() - { - let mode; - let getVal = function (aPref) - document.getElementById(aPref).value; - - if (this._checkDefaultValues(this.prefsForDefault)) { - if (getVal("browser.privatebrowsing.autostart")) - mode = "dontremember"; - else - mode = "remember"; - } - else - mode = "custom"; - - document.getElementById("historyMode").value = mode; - }, - - /** - * Update the selected pane based on the history mode menulist - */ - updateHistoryModePane: function PPP_updateHistoryModePane() - { - let selectedIndex = -1; - switch (document.getElementById("historyMode").value) { - case "remember": - selectedIndex = 0; - break; - case "dontremember": - selectedIndex = 1; - break; - case "custom": - selectedIndex = 2; - break; - } - document.getElementById("historyPane").selectedIndex = selectedIndex; - }, - - /** - * Update the private browsing auto-start pref and the history mode - * micro-management prefs based on the history mode menulist - */ - updateHistoryModePrefs: function PPP_updateHistoryModePrefs() - { - let pref = document.getElementById("browser.privatebrowsing.autostart"); - switch (document.getElementById("historyMode").value) { - case "remember": - if (pref.value) - pref.value = false; - - // select the remember history option - document.getElementById("places.history.enabled").value = true; - - // select the remember forms history option - document.getElementById("browser.formfill.enable").value = true; - - // select the accept cookies option - document.getElementById("network.cookie.cookieBehavior").value = 0; - // select the cookie lifetime policy option - document.getElementById("network.cookie.lifetimePolicy").value = 0; - - // select the clear on close option - document.getElementById("privacy.sanitize.sanitizeOnShutdown").value = false; - break; - case "dontremember": - if (!pref.value) - pref.value = true; - break; - } - }, - - /** - * Update the privacy micro-management controls based on the - * value of the private browsing auto-start checkbox. - */ - updatePrivacyMicroControls: function PPP_updatePrivacyMicroControls() - { - if (document.getElementById("historyMode").value == "custom") { - let disabled = this._autoStartPrivateBrowsing = - document.getElementById("privateBrowsingAutoStart").checked; - this.dependentControls - .forEach(function (aElement) - document.getElementById(aElement).disabled = disabled); - - const Ci = Components.interfaces; - // adjust the cookie controls status - this.readAcceptCookies(); - let lifetimePolicy = document.getElementById("network.cookie.lifetimePolicy").value; - if (lifetimePolicy != Ci.nsICookieService.ACCEPT_NORMALLY && - lifetimePolicy != Ci.nsICookieService.ACCEPT_SESSION && - lifetimePolicy != Ci.nsICookieService.ACCEPT_FOR_N_DAYS) { - lifetimePolicy = Ci.nsICookieService.ACCEPT_NORMALLY; - } - document.getElementById("keepCookiesUntil").value = disabled ? 2 : lifetimePolicy; - - // adjust the checked state of the sanitizeOnShutdown checkbox - document.getElementById("alwaysClear").checked = disabled ? false : - document.getElementById("privacy.sanitize.sanitizeOnShutdown").value; - - // adjust the checked state of the remember history checkboxes - document.getElementById("rememberHistory").checked = disabled ? false : - document.getElementById("places.history.enabled").value; - document.getElementById("rememberForms").checked = disabled ? false : - document.getElementById("browser.formfill.enable").value; - - if (!disabled) { - // adjust the Settings button for sanitizeOnShutdown - this._updateSanitizeSettingsButton(); - } - } - }, - - // PRIVATE BROWSING - - /** - * Initialize the starting state for the auto-start private browsing mode pref reverter. - */ - initAutoStartPrivateBrowsingReverter: function PPP_initAutoStartPrivateBrowsingReverter() - { - let mode = document.getElementById("historyMode"); - let autoStart = document.getElementById("privateBrowsingAutoStart"); - this._lastMode = mode.selectedIndex; - this._lastCheckState = autoStart.hasAttribute('checked'); - }, - - _lastMode: null, - _lasCheckState: null, - updateAutostart: function PPP_updateAutostart() { - let mode = document.getElementById("historyMode"); - let autoStart = document.getElementById("privateBrowsingAutoStart"); - let pref = document.getElementById("browser.privatebrowsing.autostart"); - if ((mode.value == "custom" && this._lastCheckState == autoStart.checked) || - (mode.value == "remember" && !this._lastCheckState) || - (mode.value == "dontremember" && this._lastCheckState)) { - // These are all no-op changes, so we don't need to prompt. - this._lastMode = mode.selectedIndex; - this._lastCheckState = autoStart.hasAttribute('checked'); - return; - } - - if (!this._shouldPromptForRestart) { - // We're performing a revert. Just let it happen. - return; - } - - const Cc = Components.classes, Ci = Components.interfaces; - let brandName = document.getElementById("bundleBrand").getString("brandShortName"); - let bundle = document.getElementById("bundlePreferences"); - let msg = bundle.getFormattedString(autoStart.checked ? - "featureEnableRequiresRestart" : "featureDisableRequiresRestart", - [brandName]); - let title = bundle.getFormattedString("shouldRestartTitle", [brandName]); - let prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci.nsIPromptService); - let shouldProceed = prompts.confirm(window, title, msg) - if (shouldProceed) { - let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"] - .createInstance(Ci.nsISupportsPRBool); - Services.obs.notifyObservers(cancelQuit, "quit-application-requested", - "restart"); - shouldProceed = !cancelQuit.data; - - if (shouldProceed) { - pref.value = autoStart.hasAttribute('checked'); - document.documentElement.acceptDialog(); - let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"] - .getService(Ci.nsIAppStartup); - appStartup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart); - return; - } - } - - this._shouldPromptForRestart = false; - - if (this._lastCheckState) { - autoStart.checked = "checked"; - } else { - autoStart.removeAttribute('checked'); - } - mode.selectedIndex = this._lastMode; - mode.doCommand(); - - this._shouldPromptForRestart = true; - }, - - // HISTORY - - /* - * Preferences: - * - * places.history.enabled - * - whether history is enabled or not - * browser.formfill.enable - * - true if entries in forms and the search bar should be saved, false - * otherwise - */ - - // COOKIES - - /* - * Preferences: - * - * network.cookie.cookieBehavior - * - determines how the browser should handle cookies: - * 0 means enable all cookies - * 1 means reject all third party cookies - * 2 means disable all cookies - * 3 means reject third party cookies unless at least one is already set for the eTLD - * see netwerk/cookie/src/nsCookieService.cpp for details - * network.cookie.lifetimePolicy - * - determines how long cookies are stored: - * 0 means keep cookies until they expire - * 2 means keep cookies until the browser is closed - */ - - /** - * Reads the network.cookie.cookieBehavior preference value and - * enables/disables the rest of the cookie UI accordingly, returning true - * if cookies are enabled. - */ - readAcceptCookies: function () - { - var pref = document.getElementById("network.cookie.cookieBehavior"); - var acceptThirdPartyLabel = document.getElementById("acceptThirdPartyLabel"); - var acceptThirdPartyMenu = document.getElementById("acceptThirdPartyMenu"); - var keepUntil = document.getElementById("keepUntil"); - var menu = document.getElementById("keepCookiesUntil"); - - // enable the rest of the UI for anything other than "disable all cookies" - var acceptCookies = (pref.value != 2); - - acceptThirdPartyLabel.disabled = acceptThirdPartyMenu.disabled = !acceptCookies; - keepUntil.disabled = menu.disabled = this._autoStartPrivateBrowsing || !acceptCookies; - - return acceptCookies; - }, - - /** - * Enables/disables the "keep until" label and menulist in response to the - * "accept cookies" checkbox being checked or unchecked. - */ - writeAcceptCookies: function () - { - var accept = document.getElementById("acceptCookies"); - var acceptThirdPartyMenu = document.getElementById("acceptThirdPartyMenu"); - - // if we're enabling cookies, automatically select 'accept third party always' - if (accept.checked) - acceptThirdPartyMenu.selectedIndex = 0; - - return accept.checked ? 0 : 2; - }, - - /** - * Converts between network.cookie.cookieBehavior and the third-party cookie UI - */ - readAcceptThirdPartyCookies: function () - { - var pref = document.getElementById("network.cookie.cookieBehavior"); - switch (pref.value) - { - case 0: - return "always"; - case 1: - return "never"; - case 2: - return "never"; - case 3: - return "visited"; - default: - return undefined; - } - }, - - writeAcceptThirdPartyCookies: function () - { - var accept = document.getElementById("acceptThirdPartyMenu").selectedItem; - switch (accept.value) - { - case "always": - return 0; - case "visited": - return 3; - case "never": - return 1; - default: - return undefined; - } - }, - - /** - * Displays fine-grained, per-site preferences for cookies. - */ - showCookieExceptions: function () - { - var bundlePreferences = document.getElementById("bundlePreferences"); - var params = { blockVisible : true, - sessionVisible : true, - allowVisible : true, - prefilledHost : "", - permissionType : "cookie", - windowTitle : bundlePreferences.getString("cookiepermissionstitle"), - introText : bundlePreferences.getString("cookiepermissionstext") }; - document.documentElement.openWindow("Browser:Permissions", - "chrome://browser/content/preferences/permissions.xul", - "", params); - }, - - /** - * Displays all the user's cookies in a dialog. - */ - showCookies: function (aCategory) - { - document.documentElement.openWindow("Browser:Cookies", - "chrome://browser/content/preferences/cookies.xul", - "", null); - }, - - // CLEAR PRIVATE DATA - - /* - * Preferences: - * - * privacy.sanitize.sanitizeOnShutdown - * - true if the user's private data is cleared on startup according to the - * Clear Private Data settings, false otherwise - */ - - /** - * Displays the Clear Private Data settings dialog. - */ - showClearPrivateDataSettings: function () - { - document.documentElement.openSubDialog("chrome://browser/content/preferences/sanitize.xul", - "", null); - }, - - - /** - * Displays a dialog from which individual parts of private data may be - * cleared. - */ - clearPrivateDataNow: function (aClearEverything) - { - var ts = document.getElementById("privacy.sanitize.timeSpan"); - var timeSpanOrig = ts.value; - if (aClearEverything) - ts.value = 0; - - const Cc = Components.classes, Ci = Components.interfaces; - var glue = Cc["@mozilla.org/browser/browserglue;1"] - .getService(Ci.nsIBrowserGlue); - glue.sanitize(window); - - // reset the timeSpan pref - if (aClearEverything) - ts.value = timeSpanOrig; - Services.obs.notifyObservers(null, "clear-private-data", null); - }, - - /** - * Enables or disables the "Settings..." button depending - * on the privacy.sanitize.sanitizeOnShutdown preference value - */ - _updateSanitizeSettingsButton: function () { - var settingsButton = document.getElementById("clearDataSettings"); - var sanitizeOnShutdownPref = document.getElementById("privacy.sanitize.sanitizeOnShutdown"); - - settingsButton.disabled = !sanitizeOnShutdownPref.value; - } - -}; diff --git a/components/preferences/privacy.xul b/components/preferences/privacy.xul deleted file mode 100644 index d2f8106..0000000 --- a/components/preferences/privacy.xul +++ /dev/null @@ -1,273 +0,0 @@ -<?xml version="1.0"?> - -<!-- -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- --> -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<!DOCTYPE overlay [ -<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> -<!ENTITY % privacyDTD SYSTEM "chrome://browser/locale/preferences/privacy.dtd"> -%brandDTD; -%privacyDTD; -]> - -<overlay id="PrivacyPaneOverlay" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:html="http://www.w3.org/1999/xhtml"> - - <prefpane id="panePrivacy" - onpaneload="gPrivacyPane.init();" - helpTopic="prefs-privacy"> - - <preferences id="privacyPreferences"> - - <preference id="browser.preferences.privacy.selectedTabIndex" - name="browser.preferences.privacy.selectedTabIndex" - type="int"/> - - <!-- Tracking --> - <preference id="privacy.donottrackheader.enabled" - name="privacy.donottrackheader.enabled" - type="bool"/> - - <!-- XXX button prefs --> - <preference id="pref.privacy.disable_button.cookie_exceptions" - name="pref.privacy.disable_button.cookie_exceptions" - type="bool"/> - <preference id="pref.privacy.disable_button.view_cookies" - name="pref.privacy.disable_button.view_cookies" - type="bool"/> - - <!-- Location Bar --> - <preference id="browser.urlbar.autocomplete.enabled" - name="browser.urlbar.autocomplete.enabled" - type="bool"/> - <preference id="browser.urlbar.suggest.bookmark" - name="browser.urlbar.suggest.bookmark" - type="bool"/> - <preference id="browser.urlbar.suggest.history" - name="browser.urlbar.suggest.history" - type="bool"/> - <preference id="browser.urlbar.suggest.openpage" - name="browser.urlbar.suggest.openpage" - type="bool"/> - - <!-- History --> - <preference id="places.history.enabled" - name="places.history.enabled" - type="bool"/> - <preference id="browser.formfill.enable" - name="browser.formfill.enable" - type="bool"/> - - <!-- Cookies --> - <preference id="network.cookie.cookieBehavior" name="network.cookie.cookieBehavior" type="int"/> - <preference id="network.cookie.lifetimePolicy" name="network.cookie.lifetimePolicy" type="int"/> - <preference id="network.cookie.blockFutureCookies" name="network.cookie.blockFutureCookies" type="bool"/> - - <!-- Clear Private Data --> - <preference id="privacy.sanitize.sanitizeOnShutdown" - name="privacy.sanitize.sanitizeOnShutdown" - onchange="gPrivacyPane._updateSanitizeSettingsButton();" - type="bool"/> - <preference id="privacy.sanitize.timeSpan" - name="privacy.sanitize.timeSpan" - type="int"/> - - <!-- Private Browsing --> - <preference id="browser.privatebrowsing.autostart" - name="browser.privatebrowsing.autostart" - onchange="gPrivacyPane.updatePrivacyMicroControls();" - type="bool"/> - - </preferences> - - <stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/> - - <script type="application/javascript" src="chrome://browser/content/preferences/privacy.js"/> - - <tabbox id="privacyPrefs" flex="1" - onselect="gPrivacyPane.tabSelectionChanged();"> - - <tabs id="tabsElement"> - <tab id="historyTab" label="&history.label;"/> - <tab id="trackingTab" label="&tracking.label;"/> - <tab id="locationBarTab" label="&locationBar.label;"/> - </tabs> - - <tabpanels flex="1"> - - <!-- History --> - <tabpanel id="historyPanel" orient="vertical"> - - <hbox align="center"> - <label id="historyModeLabel" - control="historyMode" - accesskey="&historyHeader.pre.accesskey;">&historyHeader.pre.label;</label> - <menulist id="historyMode" - oncommand="gPrivacyPane.updateHistoryModePane(); - gPrivacyPane.updateHistoryModePrefs(); - gPrivacyPane.updatePrivacyMicroControls(); - gPrivacyPane.updateAutostart();"> - <menupopup> - <menuitem label="&historyHeader.remember.label;" value="remember"/> - <menuitem label="&historyHeader.dontremember.label;" value="dontremember"/> - <menuitem label="&historyHeader.custom.label;" value="custom"/> - </menupopup> - </menulist> - <label>&historyHeader.post.label;</label> - </hbox> - - <deck id="historyPane"> - <vbox align="center" id="historyRememberPane"> - <hbox align="center" flex="1"> - <spacer flex="1" class="indent"/> - <vbox flex="2"> - <description>&rememberDescription.label;</description> - <separator/> - <description>&rememberActions.pre.label;<html:a - class="inline-link" href="#" - onclick="gPrivacyPane.clearPrivateDataNow(false); return false;" - >&rememberActions.clearHistory.label;</html:a>&rememberActions.middle.label;<html:a - class="inline-link" href="#" - onclick="gPrivacyPane.showCookies(); return false;" - >&rememberActions.removeCookies.label;</html:a>&rememberActions.post.label;</description> - </vbox> - <spacer flex="1" class="indent"/> - </hbox> - </vbox> - <vbox align="center" id="historyDontRememberPane"> - <hbox align="center" flex="1"> - <spacer flex="1" class="indent"/> - <vbox flex="2"> - <description>&dontrememberDescription.label;</description> - <separator/> - <description>&dontrememberActions.pre.label;<html:a - class="inline-link" href="#" - onclick="gPrivacyPane.clearPrivateDataNow(true); return false;" - >&dontrememberActions.clearHistory.label;</html:a>&dontrememberActions.post.label;</description> - </vbox> - <spacer flex="1" class="indent"/> - </hbox> - </vbox> - <vbox id="historyCustomPane"> - <separator class="thin"/> - <checkbox id="privateBrowsingAutoStart" class="indent" - label="&privateBrowsingPermanent2.label;" - accesskey="&privateBrowsingPermanent2.accesskey;" - preference="browser.privatebrowsing.autostart" - oncommand="gPrivacyPane.updateAutostart()"/> - - <vbox class="indent"> - <vbox class="indent"> - <checkbox id="rememberHistory" - label="&rememberHistory2.label;" - accesskey="&rememberHistory2.accesskey;" - preference="places.history.enabled"/> - <checkbox id="rememberForms" - label="&rememberSearchForm.label;" - accesskey="&rememberSearchForm.accesskey;" - preference="browser.formfill.enable"/> - - <hbox id="cookiesBox"> - <checkbox id="acceptCookies" label="&acceptCookies.label;" flex="1" - preference="network.cookie.cookieBehavior" - accesskey="&acceptCookies.accesskey;" - onsyncfrompreference="return gPrivacyPane.readAcceptCookies();" - onsynctopreference="return gPrivacyPane.writeAcceptCookies();"/> - <button id="cookieExceptions" oncommand="gPrivacyPane.showCookieExceptions();" - label="&cookieExceptions.label;" accesskey="&cookieExceptions.accesskey;" - preference="pref.privacy.disable_button.cookie_exceptions"/> - </hbox> - - <hbox id="acceptThirdPartyRow" class="indent"> - <hbox id="acceptThirdPartyBox" align="center"> - <label id="acceptThirdPartyLabel" control="acceptThirdPartyMenu" - accesskey="&acceptThirdParty.pre.accesskey;">&acceptThirdParty.pre.label;</label> - <menulist id="acceptThirdPartyMenu" preference="network.cookie.cookieBehavior" - onsyncfrompreference="return gPrivacyPane.readAcceptThirdPartyCookies();" - onsynctopreference="return gPrivacyPane.writeAcceptThirdPartyCookies();"> - <menupopup> - <menuitem label="&acceptThirdParty.always.label;" value="always"/> - <menuitem label="&acceptThirdParty.visited.label;" value="visited"/> - <menuitem label="&acceptThirdParty.never.label;" value="never"/> - </menupopup> - </menulist> - </hbox> - </hbox> - - <hbox id="keepRow" class="indent"> - <hbox id="keepBox" align="center"> - <label id="keepUntil" - control="keepCookiesUntil" - accesskey="&keepUntil.accesskey;">&keepUntil.label;</label> - <menulist id="keepCookiesUntil" - preference="network.cookie.lifetimePolicy"> - <menupopup> - <menuitem label="&expire.label;" value="0"/> - <menuitem label="&close.label;" value="2"/> - </menupopup> - </menulist> - </hbox> - <hbox flex="1"/> - <button id="showCookiesButton" - label="&showCookies.label;" accesskey="&showCookies.accesskey;" - oncommand="gPrivacyPane.showCookies();" - preference="pref.privacy.disable_button.view_cookies"/> - </hbox> - - <hbox id="clearDataBox" align="center"> - <checkbox id="alwaysClear" flex="1" - preference="privacy.sanitize.sanitizeOnShutdown" - label="&clearOnClose.label;" - accesskey="&clearOnClose.accesskey;"/> - <button id="clearDataSettings" label="&clearOnCloseSettings.label;" - accesskey="&clearOnCloseSettings.accesskey;" - oncommand="gPrivacyPane.showClearPrivateDataSettings();"/> - </hbox> - </vbox> - </vbox> - </vbox> - </deck> - - </tabpanel> - - <!-- Tracking --> - <tabpanel id="trackingPanel" orient="vertical"> - - <checkbox id="privacyDoNotTrackCheckbox" - label="&dntTrackingNotOkay.label2;" - accesskey="&dntTrackingNotOkay.accesskey;" - preference="privacy.donottrackheader.enabled"/> - <separator class="thin"/> - <label class="text-link" id="doNotTrackInfo" - href="https://www.mozilla.org/dnt" - value="&doNotTrackInfo.label;"/> - - </tabpanel> - - <!-- Location Bar --> - <tabpanel id="locatioBarPanel" orient="vertical"> - - <label id="locationBarSuggestionLabel">&locbar.suggest.label;</label> - - <vbox id="tabPrefsBox" align="start" flex="1"> - <checkbox id="historySuggestion" label="&locbar.history.label;" - accesskey="&locbar.history.accesskey;" - preference="browser.urlbar.suggest.history"/> - <checkbox id="bookmarkSuggestion" label="&locbar.bookmarks.label;" - accesskey="&locbar.bookmarks.accesskey;" - preference="browser.urlbar.suggest.bookmark"/> - <checkbox id="openpageSuggestion" label="&locbar.openpage.label;" - accesskey="&locbar.openpage.accesskey;" - preference="browser.urlbar.suggest.openpage"/> - </vbox> - - </tabpanel> - - </tabpanels> - </tabbox> - </prefpane> - -</overlay> diff --git a/components/preferences/sanitize.js b/components/preferences/sanitize.js deleted file mode 100644 index 15e6f58..0000000 --- a/components/preferences/sanitize.js +++ /dev/null @@ -1,12 +0,0 @@ -/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var gSanitizeDialog = Object.freeze({ - onClearHistoryChanged: function () { - let downloadsPref = document.getElementById("privacy.clearOnShutdown.downloads"); - let historyPref = document.getElementById("privacy.clearOnShutdown.history"); - downloadsPref.value = historyPref.value; - } -}); diff --git a/components/preferences/sanitize.xul b/components/preferences/sanitize.xul deleted file mode 100644 index 829b5df..0000000 --- a/components/preferences/sanitize.xul +++ /dev/null @@ -1,108 +0,0 @@ -<?xml version="1.0"?> - -<!-- -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- --> -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<?xml-stylesheet href="chrome://global/skin/"?> -<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?> - -<!DOCTYPE dialog [ - <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> - <!ENTITY % sanitizeDTD SYSTEM "chrome://browser/locale/sanitize.dtd"> - %brandDTD; - %sanitizeDTD; -]> - -<prefwindow id="SanitizeDialog" type="child" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - dlgbuttons="accept,cancel,help" - ondialoghelp="openPrefsHelp()" - style="width: &dialog.width2;;" - title="&sanitizePrefs2.title;" - onload="gSanitizeDialog.onClearHistoryChanged();"> - - <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/> - <script type="application/javascript" src="chrome://browser/content/preferences/sanitize.js"/> - - <prefpane id="SanitizeDialogPane" - helpTopic="prefs-clear-private-data"> - - <preferences> - <preference id="privacy.clearOnShutdown.history" name="privacy.clearOnShutdown.history" type="bool" - onchange="return gSanitizeDialog.onClearHistoryChanged();"/> - <preference id="privacy.clearOnShutdown.formdata" name="privacy.clearOnShutdown.formdata" type="bool"/> - <preference id="privacy.clearOnShutdown.passwords" name="privacy.clearOnShutdown.passwords" type="bool"/> - <preference id="privacy.clearOnShutdown.downloads" name="privacy.clearOnShutdown.downloads" type="bool"/> - <preference id="privacy.clearOnShutdown.cookies" name="privacy.clearOnShutdown.cookies" type="bool"/> - <preference id="privacy.clearOnShutdown.cache" name="privacy.clearOnShutdown.cache" type="bool"/> - <preference id="privacy.clearOnShutdown.offlineApps" name="privacy.clearOnShutdown.offlineApps" type="bool"/> - <preference id="privacy.clearOnShutdown.sessions" name="privacy.clearOnShutdown.sessions" type="bool"/> - <preference id="privacy.clearOnShutdown.siteSettings" name="privacy.clearOnShutdown.siteSettings" type="bool"/> - <preference id="privacy.clearOnShutdown.connectivityData" name="privacy.clearOnShutdown.connectivityData" type="bool"/> - </preferences> - - <description>&clearDataSettings2.label;</description> - - <groupbox orient="horizontal"> - <caption label="&historySection.label;"/> - <grid flex="1"> - <columns> - <column style="width: &column.width2;"/> - <column flex="1"/> - </columns> - <rows> - <row> - <checkbox label="&itemHistoryAndDownloads.label;" - accesskey="&itemHistoryAndDownloads.accesskey;" - preference="privacy.clearOnShutdown.history"/> - <checkbox label="&itemCookies.label;" - accesskey="&itemCookies.accesskey;" - preference="privacy.clearOnShutdown.cookies"/> - </row> - <row> - <checkbox label="&itemActiveLogins.label;" - accesskey="&itemActiveLogins.accesskey;" - preference="privacy.clearOnShutdown.sessions"/> - <checkbox label="&itemCache.label;" - accesskey="&itemCache.accesskey;" - preference="privacy.clearOnShutdown.cache"/> - </row> - <row> - <checkbox label="&itemFormSearchHistory.label;" - accesskey="&itemFormSearchHistory.accesskey;" - preference="privacy.clearOnShutdown.formdata"/> - </row> - </rows> - </grid> - </groupbox> - <groupbox orient="horizontal"> - <caption label="&dataSection.label;"/> - <grid flex="1"> - <columns> - <column style="width: &column.width2;"/> - <column flex="1"/> - </columns> - <rows> - <row> - <checkbox label="&itemPasswords.label;" - accesskey="&itemPasswords.accesskey;" - preference="privacy.clearOnShutdown.passwords"/> - <checkbox label="&itemOfflineApps.label;" - accesskey="&itemOfflineApps.accesskey;" - preference="privacy.clearOnShutdown.offlineApps"/> - </row> - <row> - <checkbox label="&itemSitePreferences.label;" - accesskey="&itemSitePreferences.accesskey;" - preference="privacy.clearOnShutdown.siteSettings"/> - <checkbox label="&itemConnectivityData.label;" - accesskey="&itemConnectivityData.accesskey;" - preference="privacy.clearOnShutdown.connectivityData"/> - </row> - </rows> - </grid> - </groupbox> - </prefpane> -</prefwindow> diff --git a/components/preferences/security.js b/components/preferences/security.js deleted file mode 100644 index a0f283d..0000000 --- a/components/preferences/security.js +++ /dev/null @@ -1,251 +0,0 @@ -/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper", - "resource://gre/modules/LoginHelper.jsm"); - -Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); - -var gSecurityPane = { - _pane: null, - - /** - * Initializes UI. - */ - init: function () - { - this._pane = document.getElementById("paneSecurity"); - this._initMasterPasswordUI(); - }, - - // ADD-ONS - - /* - * Preferences: - * - * xpinstall.whitelist.required - * - true if a site must be added to a site whitelist before extensions - * provided by the site may be installed from it, false if the extension - * may be directly installed after a confirmation dialog - */ - - /** - * Enables/disables the add-ons Exceptions button depending on whether - * or not add-on installation warnings are displayed. - */ - readWarnAddonInstall: function () - { - var warn = document.getElementById("xpinstall.whitelist.required"); - var exceptions = document.getElementById("addonExceptions"); - - exceptions.disabled = !warn.value; - - // don't override the preference value - return undefined; - }, - - /** - * Displays the exceptions lists for add-on installation warnings. - */ - showAddonExceptions: function () - { - var bundlePrefs = document.getElementById("bundlePreferences"); - - var params = this._addonParams; - if (!params.windowTitle || !params.introText) { - params.windowTitle = bundlePrefs.getString("addons_permissions_title"); - params.introText = bundlePrefs.getString("addonspermissionstext"); - } - - document.documentElement.openWindow("Browser:Permissions", - "chrome://browser/content/preferences/permissions.xul", - "", params); - }, - - /** - * Parameters for the add-on install permissions dialog. - */ - _addonParams: - { - blockVisible: false, - sessionVisible: false, - allowVisible: true, - prefilledHost: "", - permissionType: "install" - }, - - /** - * Ensures that the blocklist is enabled/disabled appropriately based on level - */ - addonLevelNeedsSync: function() - { - Services.prefs.setBoolPref("extensions.blocklist.level.updated", true); - }, - // called from preferences window onunload. - syncAddonSecurityLevel: function() - { - if (Services.prefs.getBoolPref("extensions.blocklist.level.updated") == true) { - Services.prefs.setBoolPref("extensions.blocklist.level.updated", false); - var secLevel = Services.prefs.getIntPref("extensions.blocklist.level"); - Services.prefs.setBoolPref("extensions.blocklist.enabled", - !(secLevel == 99)); - } - }, - - // PASSWORDS - - /* - * Preferences: - * - * signon.rememberSignons - * - true if passwords are remembered, false otherwise - */ - - /** - * Enables/disables the Exceptions button used to configure sites where - * passwords are never saved. When browser is set to start in Private - * Browsing mode, the "Remember passwords" UI is useless, so we disable it. - */ - readSavePasswords: function () - { - var pref = document.getElementById("signon.rememberSignons"); - var excepts = document.getElementById("passwordExceptions"); - - if (PrivateBrowsingUtils.permanentPrivateBrowsing) { - document.getElementById("savePasswords").disabled = true; - excepts.disabled = true; - return false; - } else { - excepts.disabled = !pref.value; - // don't override pref value in UI - return undefined; - } - }, - - /** - * Displays a dialog in which the user can view and modify the list of sites - * where passwords are never saved. - */ - showPasswordExceptions: function () - { - let bundlePrefs = document.getElementById("bundlePreferences"); - let params = { - blockVisible: true, - sessionVisible: false, - allowVisible: false, - hideStatusColumn: true, - prefilledHost: "", - permissionType: "login-saving", - windowTitle: bundlePrefs.getString("savedLoginsExceptions_title"), - introText: bundlePrefs.getString("savedLoginsExceptions_desc") - }; - - document.documentElement.openWindow("Toolkit:PasswordManagerExceptions", - "chrome://browser/content/preferences/permissions.xul", - null, params); - }, - - /** - * Initializes master password UI: the "use master password" checkbox, selects - * the master password button to show, and enables/disables it as necessary. - * The master password is controlled by various bits of NSS functionality, so - * the UI for it can't be controlled by the normal preference bindings. - */ - _initMasterPasswordUI: function () - { - var noMP = !LoginHelper.isMasterPasswordSet(); - - var button = document.getElementById("changeMasterPassword"); - button.disabled = noMP; - - var checkbox = document.getElementById("useMasterPassword"); - checkbox.checked = !noMP; - }, - - /** - * Enables/disables the master password button depending on the state of the - * "use master password" checkbox, and prompts for master password removal if - * one is set. - */ - updateMasterPasswordButton: function () - { - var checkbox = document.getElementById("useMasterPassword"); - var button = document.getElementById("changeMasterPassword"); - button.disabled = !checkbox.checked; - - // unchecking the checkbox should try to immediately remove the master - // password, because it's impossible to non-destructively remove the master - // password used to encrypt all the passwords without providing it (by - // design), and it would be extremely odd to pop up that dialog when the - // user closes the prefwindow and saves his settings - if (!checkbox.checked) - this._removeMasterPassword(); - else - this.changeMasterPassword(); - - this._initMasterPasswordUI(); - }, - - /** - * Displays the "remove master password" dialog to allow the user to remove - * the current master password. When the dialog is dismissed, master password - * UI is automatically updated. - */ - _removeMasterPassword: function () - { - const Cc = Components.classes, Ci = Components.interfaces; - var secmodDB = Cc["@mozilla.org/security/pkcs11moduledb;1"]. - getService(Ci.nsIPKCS11ModuleDB); - if (secmodDB.isFIPSEnabled) { - var promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"]. - getService(Ci.nsIPromptService); - var bundle = document.getElementById("bundlePreferences"); - promptService.alert(window, - bundle.getString("pw_change_failed_title"), - bundle.getString("pw_change2empty_in_fips_mode")); - } - else { - document.documentElement.openSubDialog("chrome://mozapps/content/preferences/removemp.xul", - "", null); - } - this._initMasterPasswordUI(); - }, - - /** - * Displays a dialog in which the master password may be changed. - */ - changeMasterPassword: function () - { - document.documentElement.openSubDialog("chrome://mozapps/content/preferences/changemp.xul", - "", null); - this._initMasterPasswordUI(); - }, - - /** - * Shows the sites where the user has saved passwords and the associated login - * information. - */ - showPasswords: function () - { - document.documentElement.openWindow("Toolkit:PasswordManager", - "chrome://passwordmgr/content/passwordManager.xul", - "", null); - }, - - /** - * Updates the HPKP enforcement level to the proper value depending on checkbox - * state. - */ - updateHPKPPref: function() { - let checkbox = document.getElementById("enableHPKP"); - let HPKPpref = document.getElementById("security.cert_pinning.enforcement_level"); - - if (checkbox.checked) { - HPKPpref.value = 2; - } else { - HPKPpref.value = 0; - } - } -}; diff --git a/components/preferences/security.xul b/components/preferences/security.xul deleted file mode 100644 index bae6cca..0000000 --- a/components/preferences/security.xul +++ /dev/null @@ -1,181 +0,0 @@ -<?xml version="1.0"?> - -<!-- -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- --> -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<!DOCTYPE overlay [ - <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> - <!ENTITY % securityDTD SYSTEM "chrome://browser/locale/preferences/security.dtd"> - %brandDTD; - %securityDTD; -]> - -<overlay id="SecurityPaneOverlay" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - - <prefpane id="paneSecurity" - onpaneload="gSecurityPane.init();" - helpTopic="prefs-security"> - - <preferences id="securityPreferences"> - <!-- XXX buttons --> - <preference id="pref.privacy.disable_button.view_passwords" - name="pref.privacy.disable_button.view_passwords" - type="bool"/> - <preference id="pref.privacy.disable_button.view_passwords_exceptions" - name="pref.privacy.disable_button.view_passwords_exceptions" - type="bool"/> - - <!-- Add-ons, malware, phishing --> - <preference id="xpinstall.whitelist.required" - name="xpinstall.whitelist.required" - type="bool"/> - <preference id="extensions.blocklist.level" - name="extensions.blocklist.level" - onchange="gSecurityPane.addonLevelNeedsSync();" - type="int"/> - - <!-- Passwords --> - <preference id="signon.rememberSignons" name="signon.rememberSignons" type="bool"/> - <preference id="signon.autofillForms" name="signon.autofillForms" type="bool"/> - - <!-- Security Protocols --> - - <preference id="network.stricttransportsecurity.enabled" - name="network.stricttransportsecurity.enabled" - type="bool"/> - - <!-- Opportunistic Encryption --> - - <preference id="network.http.upgrade-insecure-requests" - name="network.http.upgrade-insecure-requests" - type="bool"/> - <preference id="network.http.altsvc.oe" - name="network.http.altsvc.oe" - type="bool"/> - - <!-- XSS Filter --> - <!-- - <preference id="security.xssfilter.enable" name="security.xssfilter.enable" type="bool"/> - --> - - </preferences> - - <script type="application/javascript" src="chrome://browser/content/preferences/security.js"/> - - <stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/> - - <!-- addons, forgery (phishing) UI --> - <groupbox id="addonsSecurityGroup"> - <caption label="&addons.label;"/> - - <hbox id="addonInstallBox"> - <checkbox id="warnAddonInstall" flex="1" - label="&warnAddonInstall.label;" - accesskey="&warnAddonInstall.accesskey;" - preference="xpinstall.whitelist.required" - onsyncfrompreference="return gSecurityPane.readWarnAddonInstall();"/> - <button id="addonExceptions" - label="&addonExceptions.label;" - accesskey="&addonExceptions.accesskey;" - oncommand="gSecurityPane.showAddonExceptions();"/> - </hbox> - <hbox id="addonSecuritySettingsBox" flex="1"> - <vbox> - <label id="addonSecurity" control="addonsecurity-menu">&addonSecuritylevel;</label> - <menulist id="addonsecurity-menu" preference="extensions.blocklist.level" sizetopopup="always"> - <menupopup> - <menuitem label="&addonSecurityLevel_Off;" value="99" /> - <menuitem label="&addonSecurityLevel_Low;" value="3" /> - <menuitem label="&addonSecurityLevel_High;" value="2" /> - <menuitem label="&addonSecurityLevel_Extreme;" value="1" /> - </menupopup> - </menulist> - </vbox> - </hbox> - </groupbox> - - <!-- Passwords --> - <groupbox id="passwordsGroup" orient="vertical"> - <caption label="&passwords.label;"/> - - <hbox id="savePasswordsBox"> - <checkbox id="savePasswords" flex="1" - label="&rememberPasswords.label;" accesskey="&rememberPasswords.accesskey;" - preference="signon.rememberSignons" - onsyncfrompreference="return gSecurityPane.readSavePasswords();"/> - <button id="passwordExceptions" - label="&passwordExceptions.label;" - accesskey="&passwordExceptions.accesskey;" - oncommand="gSecurityPane.showPasswordExceptions();" - preference="pref.privacy.disable_button.view_passwords_exceptions"/> - </hbox> - <checkbox id="autofillPasswords" flex="1" - label="&autofillPasswords.label;" accesskey="&autofillPasswords.accesskey;" - preference="signon.autofillForms"/> - <hbox id="masterPasswordBox"> - <checkbox id="useMasterPassword" flex="1" - oncommand="gSecurityPane.updateMasterPasswordButton();" - label="&useMasterPassword.label;" - accesskey="&useMasterPassword.accesskey;"/> - <button id="changeMasterPassword" - label="&changeMasterPassword.label;" - accesskey="&changeMasterPassword.accesskey;" - oncommand="gSecurityPane.changeMasterPassword();"/> - </hbox> - - <hbox id="showPasswordsBox"> - <spacer flex="1"/> - <button id="showPasswords" - label="&savedPasswords.label;" accesskey="&savedPasswords.accesskey;" - oncommand="gSecurityPane.showPasswords();" - preference="pref.privacy.disable_button.view_passwords"/> - </hbox> - </groupbox> - - <!-- Security protocols --> - <groupbox id="SecProtoGroup"> - <caption label="&SecProto.label;"/> - - <vbox id="SecProtoBox" align="start" flex="1"> - <checkbox id="enableHSTS" - label="&enableHSTS.label;" - accesskey="&enableHSTS.accesskey;" - preference="network.stricttransportsecurity.enabled" /> - <checkbox id="enableHPKP" - label="&enableHPKP.label;" - accesskey="&enableHPKP.accesskey;" - preference="security.cert_pinning.hpkp.enabled"/> - </vbox> - </groupbox> - - <groupbox id="OpportunisticEncryption"> - <caption label="&OpEnc.label;"/> - <checkbox id="enableUIROpEnc" - label="&enableUIROpEnc.label;" - preference="network.http.upgrade-insecure-requests" /> - <checkbox id="enableAltSvcOpEnc" - label="&enableAltSvcOpEnc.label;" - preference="network.http.altsvc.oe" /> - </groupbox> - - <!-- XSS Filter --> - <!-- - <groupbox id="XSSFiltGroup"> - <caption label="&XSSFilt.label;"/> - - <hbox id="XSSFiltBox"> - <checkbox id="enableXSSFilt" flex="1" - label="&enableXSSFilt.label;" - accesskey="&enableXSSFilt.accesskey;" - preference="security.xssfilter.enable" /> - </hbox> - - </groupbox> - --> - - </prefpane> - -</overlay> diff --git a/components/preferences/selectBookmark.js b/components/preferences/selectBookmark.js deleted file mode 100644 index c7ce022..0000000 --- a/components/preferences/selectBookmark.js +++ /dev/null @@ -1,83 +0,0 @@ -//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * SelectBookmarkDialog controls the user interface for the "Use Bookmark for - * Home Page" dialog. - * - * The caller (gMainPane.setHomePageToBookmark in main.js) invokes this dialog - * with a single argument - a reference to an object with a .urls property and - * a .names property. This dialog is responsible for updating the contents of - * the .urls property with an array of URLs to use as home pages and for - * updating the .names property with an array of names for those URLs before it - * closes. - */ -var SelectBookmarkDialog = { - init: function SBD_init() { - document.getElementById("bookmarks").place = - "place:queryType=1&folder=" + PlacesUIUtils.allBookmarksFolderId; - - // Initial update of the OK button. - this.selectionChanged(); - }, - - /** - * Update the disabled state of the OK button as the user changes the - * selection within the view. - */ - selectionChanged: function SBD_selectionChanged() { - var accept = document.documentElement.getButton("accept"); - var bookmarks = document.getElementById("bookmarks"); - var disableAcceptButton = true; - if (bookmarks.hasSelection) { - if (!PlacesUtils.nodeIsSeparator(bookmarks.selectedNode)) - disableAcceptButton = false; - } - accept.disabled = disableAcceptButton; - }, - - onItemDblClick: function SBD_onItemDblClick() { - var bookmarks = document.getElementById("bookmarks"); - var selectedNode = bookmarks.selectedNode; - if (selectedNode && PlacesUtils.nodeIsURI(selectedNode)) { - /** - * The user has double clicked on a tree row that is a link. Take this to - * mean that they want that link to be their homepage, and close the dialog. - */ - document.documentElement.getButton("accept").click(); - } - }, - - /** - * User accepts their selection. Set all the selected URLs or the contents - * of the selected folder as the list of homepages. - */ - accept: function SBD_accept() { - var bookmarks = document.getElementById("bookmarks"); - NS_ASSERT(bookmarks.hasSelection, - "Should not be able to accept dialog if there is no selected URL!"); - var urls = []; - var names = []; - var selectedNode = bookmarks.selectedNode; - if (PlacesUtils.nodeIsFolder(selectedNode)) { - var contents = PlacesUtils.getFolderContents(selectedNode.itemId).root; - var cc = contents.childCount; - for (var i = 0; i < cc; ++i) { - var node = contents.getChild(i); - if (PlacesUtils.nodeIsURI(node)) { - urls.push(node.uri); - names.push(node.title); - } - } - contents.containerOpen = false; - } - else { - urls.push(selectedNode.uri); - names.push(selectedNode.title); - } - window.arguments[0].urls = urls; - window.arguments[0].names = names; - } -}; diff --git a/components/preferences/selectBookmark.xul b/components/preferences/selectBookmark.xul deleted file mode 100644 index 5547534..0000000 --- a/components/preferences/selectBookmark.xul +++ /dev/null @@ -1,44 +0,0 @@ -<?xml version="1.0"?> -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - - -<?xml-stylesheet href="chrome://browser/content/places/places.css"?> - -<?xml-stylesheet href="chrome://global/skin/"?> -<?xml-stylesheet href="chrome://browser/skin/places/places.css"?> - -<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?> - -<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/selectBookmark.dtd"> - -<dialog id="selectBookmarkDialog" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - title="&selectBookmark.title;" style="width: 32em;" - persist="screenX screenY width height" screenX="24" screenY="24" - onload="SelectBookmarkDialog.init();" - ondialogaccept="SelectBookmarkDialog.accept();"> - - <script type="application/javascript" - src="chrome://browser/content/preferences/selectBookmark.js"/> - - <description>&selectBookmark.label;</description> - - <separator class="thin"/> - - <tree id="bookmarks" flex="1" type="places" - style="height: 15em;" - hidecolumnpicker="true" - seltype="single" - ondblclick="SelectBookmarkDialog.onItemDblClick();" - onselect="SelectBookmarkDialog.selectionChanged();"> - <treecols> - <treecol id="title" flex="1" primary="true" hideheader="true"/> - </treecols> - <treechildren id="bookmarksChildren" flex="1"/> - </tree> - - <separator class="thin"/> - -</dialog> diff --git a/components/preferences/sync.js b/components/preferences/sync.js deleted file mode 100644 index f29728d..0000000 --- a/components/preferences/sync.js +++ /dev/null @@ -1,192 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -Components.utils.import("resource://services-sync/main.js"); -Components.utils.import("resource://gre/modules/Services.jsm"); - -const PAGE_NO_ACCOUNT = 0; -const PAGE_HAS_ACCOUNT = 1; -const PAGE_NEEDS_UPDATE = 2; - -var gSyncPane = { - _stringBundle: null, - prefArray: ["engine.bookmarks", "engine.passwords", "engine.prefs", - "engine.tabs", "engine.history"], - - get page() { - return document.getElementById("weavePrefsDeck").selectedIndex; - }, - - set page(val) { - document.getElementById("weavePrefsDeck").selectedIndex = val; - }, - - get _usingCustomServer() { - return Weave.Svc.Prefs.isSet("serverURL"); - }, - - needsUpdate: function () { - this.page = PAGE_NEEDS_UPDATE; - let label = document.getElementById("loginError"); - label.value = Weave.Utils.getErrorString(Weave.Status.login); - label.className = "error"; - }, - - init: function () { - // If the Service hasn't finished initializing, wait for it. - let xps = Components.classes["@mozilla.org/weave/service;1"] - .getService(Components.interfaces.nsISupports) - .wrappedJSObject; - - if (xps.ready) { - this._init(); - return; - } - - let onUnload = function () { - window.removeEventListener("unload", onUnload, false); - try { - Services.obs.removeObserver(onReady, "weave:service:ready"); - } catch (e) {} - }; - - let onReady = function () { - Services.obs.removeObserver(onReady, "weave:service:ready"); - window.removeEventListener("unload", onUnload, false); - this._init(); - }.bind(this); - - Services.obs.addObserver(onReady, "weave:service:ready", false); - window.addEventListener("unload", onUnload, false); - - xps.ensureLoaded(); - }, - - _init: function () { - let topics = ["weave:service:login:error", - "weave:service:login:finish", - "weave:service:start-over", - "weave:service:setup-complete", - "weave:service:logout:finish"]; - - // Add the observers now and remove them on unload - //XXXzpao This should use Services.obs.* but Weave's Obs does nice handling - // of `this`. Fix in a followup. (bug 583347) - topics.forEach(function (topic) { - Weave.Svc.Obs.add(topic, this.updateWeavePrefs, this); - }, this); - window.addEventListener("unload", function() { - topics.forEach(function (topic) { - Weave.Svc.Obs.remove(topic, this.updateWeavePrefs, this); - }, gSyncPane); - }, false); - - this._stringBundle = - Services.strings.createBundle("chrome://browser/locale/preferences/preferences.properties"); - this.updateWeavePrefs(); - }, - - updateWeavePrefs: function () { - if (Weave.Status.service == Weave.CLIENT_NOT_CONFIGURED || - Weave.Svc.Prefs.get("firstSync", "") == "notReady") { - this.page = PAGE_NO_ACCOUNT; - } else if (Weave.Status.login == Weave.LOGIN_FAILED_INVALID_PASSPHRASE || - Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED) { - this.needsUpdate(); - } else { - this.page = PAGE_HAS_ACCOUNT; - document.getElementById("accountName").value = Weave.Service.identity.account; - document.getElementById("syncComputerName").value = Weave.Service.clientsEngine.localName; - document.getElementById("tosPP").hidden = this._usingCustomServer; - } - }, - - startOver: function (showDialog) { - if (showDialog) { - let flags = Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_IS_STRING + - Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_CANCEL + - Services.prompt.BUTTON_POS_1_DEFAULT; - let buttonChoice = - Services.prompt.confirmEx(window, - this._stringBundle.GetStringFromName("syncUnlink.title"), - this._stringBundle.GetStringFromName("syncUnlink.label"), - flags, - this._stringBundle.GetStringFromName("syncUnlinkConfirm.label"), - null, null, null, {}); - - // If the user selects cancel, just bail - if (buttonChoice == 1) { - return; - } - } - - Weave.Service.startOver(); - this.updateWeavePrefs(); - }, - - updatePass: function () { - if (Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED) { - gSyncUtils.changePassword(); - } else { - gSyncUtils.updatePassphrase(); - } - }, - - resetPass: function () { - if (Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED) { - gSyncUtils.resetPassword(); - } else { - gSyncUtils.resetPassphrase(); - } - }, - - /** - * Invoke the Sync setup wizard. - * - * @param wizardType - * Indicates type of wizard to launch: - * null -- regular set up wizard - * "pair" -- pair a device first - * "reset" -- reset sync - */ - openSetup: function (wizardType) { - let win = Services.wm.getMostRecentWindow("Weave:AccountSetup"); - if (win) { - win.focus(); - } else { - window.openDialog("chrome://browser/content/sync/setup.xul", - "weaveSetup", "centerscreen,chrome,resizable=no", - wizardType); - } - }, - - openQuotaDialog: function () { - let win = Services.wm.getMostRecentWindow("Sync:ViewQuota"); - if (win) { - win.focus(); - } else { - window.openDialog("chrome://browser/content/sync/quota.xul", "", - "centerscreen,chrome,dialog,modal"); - } - }, - - openAddDevice: function () { - if (!Weave.Utils.ensureMPUnlocked()) { - return; - } - - let win = Services.wm.getMostRecentWindow("Sync:AddDevice"); - if (win) { - win.focus(); - } else { - window.openDialog("chrome://browser/content/sync/addDevice.xul", - "syncAddDevice", "centerscreen,chrome,resizable=no"); - } - }, - - resetSync: function () { - this.openSetup("reset"); - }, -}; - diff --git a/components/preferences/sync.xul b/components/preferences/sync.xul deleted file mode 100644 index 52f5a95..0000000 --- a/components/preferences/sync.xul +++ /dev/null @@ -1,178 +0,0 @@ -<?xml version="1.0"?> - -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<!DOCTYPE overlay [ -<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> -<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd"> -<!ENTITY % syncDTD SYSTEM "chrome://browser/locale/preferences/sync.dtd"> -%brandDTD; -%syncBrandDTD; -%syncDTD; -]> - -<overlay id="SyncPaneOverlay" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:html="http://www.w3.org/1999/xhtml"> - - <prefpane id="paneSync" - helpTopic="prefs-weave" - onpaneload="gSyncPane.init()"> - - <preferences> -<!-- <preference id="engine.addons" name="services.sync.engine.addons" type="bool"/> --> - <preference id="engine.bookmarks" name="services.sync.engine.bookmarks" type="bool"/> - <preference id="engine.history" name="services.sync.engine.history" type="bool"/> - <preference id="engine.tabs" name="services.sync.engine.tabs" type="bool"/> - <preference id="engine.prefs" name="services.sync.engine.prefs" type="bool"/> - <preference id="engine.passwords" name="services.sync.engine.passwords" type="bool"/> - </preferences> - - - <script type="application/javascript" - src="chrome://browser/content/preferences/sync.js"/> - <script type="application/javascript" - src="chrome://browser/content/sync/utils.js"/> - - - <deck id="weavePrefsDeck"> - <vbox id="noAccount" align="center"> - <spacer flex="1"/> - <description id="syncDesc"> - &weaveDesc.label; - </description> - <separator/> - <label class="text-link" - onclick="event.stopPropagation(); gSyncPane.openSetup(null);" - value="&setupButton.label;"/> - <separator/> - <label class="text-link" - onclick="event.stopPropagation(); gSyncPane.openSetup('pair');" - value="&pairDevice.label;"/> - <spacer flex="3"/> - </vbox> - - <vbox id="hasAccount"> - <groupbox class="syncGroupBox"> - <!-- label is set to account name --> - <caption id="accountCaption" align="center"> - <image id="accountCaptionImage"/> - <label id="accountName" value=""/> - </caption> - - <hbox> - <button type="menu" - label="&manageAccount.label;" - accesskey="&manageAccount.accesskey;"> - <menupopup> - <menuitem label="&viewQuota.label;" - oncommand="gSyncPane.openQuotaDialog();"/> - <menuseparator/> - <menuitem label="&changePassword2.label;" - oncommand="gSyncUtils.changePassword();"/> - <menuitem label="&myRecoveryKey.label;" - oncommand="gSyncUtils.resetPassphrase();"/> - <menuseparator/> - <menuitem label="&resetSync2.label;" - oncommand="gSyncPane.resetSync();"/> - </menupopup> - </button> - </hbox> - - <hbox> - <label id="syncAddDeviceLabel" - class="text-link" - onclick="gSyncPane.openAddDevice(); return false;" - value="&pairDevice.label;"/> - </hbox> - - <vbox> - <label value="&syncMy.label;" /> - <richlistbox id="syncEnginesList" - orient="vertical" - onselect="if (this.selectedCount) this.clearSelection();"> -<!-- <richlistitem> - <checkbox label="&engine.addons.label;" - accesskey="&engine.addons.accesskey;" - preference="engine.addons"/> - </richlistitem> --> - <richlistitem> - <checkbox label="&engine.bookmarks.label;" - accesskey="&engine.bookmarks.accesskey;" - preference="engine.bookmarks"/> - </richlistitem> - <richlistitem> - <checkbox label="&engine.passwords.label;" - accesskey="&engine.passwords.accesskey;" - preference="engine.passwords"/> - </richlistitem> - <richlistitem> - <checkbox label="&engine.prefs.label;" - accesskey="&engine.prefs.accesskey;" - preference="engine.prefs"/> - </richlistitem> - <richlistitem> - <checkbox label="&engine.history.label;" - accesskey="&engine.history.accesskey;" - preference="engine.history"/> - </richlistitem> - <richlistitem> - <checkbox label="&engine.tabs.label;" - accesskey="&engine.tabs.accesskey;" - preference="engine.tabs"/> - </richlistitem> - </richlistbox> - </vbox> - </groupbox> - - <groupbox class="syncGroupBox"> - <grid> - <columns> - <column/> - <column flex="1"/> - </columns> - <rows> - <row align="center"> - <label value="&syncDeviceName.label;" - accesskey="&syncDeviceName.accesskey;" - control="syncComputerName"/> - <textbox id="syncComputerName" - onchange="gSyncUtils.changeName(this)"/> - </row> - </rows> - </grid> - <hbox> - <label class="text-link" - onclick="gSyncPane.startOver(true); return false;" - value="&unlinkDevice.label;"/> - </hbox> - </groupbox> - <hbox id="tosPP" pack="center"> - <label class="text-link" - onclick="event.stopPropagation();gSyncUtils.openToS();" - value="&prefs.tosLink.label;"/> - <label class="text-link" - onclick="event.stopPropagation();gSyncUtils.openPrivacyPolicy();" - value="&prefs.ppLink.label;"/> - </hbox> - </vbox> - - <vbox id="needsUpdate" align="center" pack="center"> - <hbox> - <label id="loginError" value=""/> - <label class="text-link" - onclick="gSyncPane.updatePass(); return false;" - value="&updatePass.label;"/> - <label class="text-link" - onclick="gSyncPane.resetPass(); return false;" - value="&resetPass.label;"/> - </hbox> - <label class="text-link" - onclick="gSyncPane.startOver(true); return false;" - value="&unlinkDevice.label;"/> - </vbox> - </deck> - </prefpane> -</overlay> diff --git a/components/preferences/tabs.js b/components/preferences/tabs.js deleted file mode 100644 index b09cb60..0000000 --- a/components/preferences/tabs.js +++ /dev/null @@ -1,90 +0,0 @@ -# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -var gTabsPane = { - - /* - * Preferences: - * - * browser.link.open_newwindow - * - determines where pages which would open in a new window are opened: - * 1 opens such links in the most recent window or tab, - * 2 opens such links in a new window, - * 3 opens such links in a new tab - * browser.tabs.loadInBackground - * - true if display should switch to a new tab which has been opened from a - * link, false if display shouldn't switch - * browser.tabs.warnOnClose - * - true if when closing a window with multiple tabs the user is warned and - * allowed to cancel the action, false to just close the window - * browser.tabs.warnOnOpen - * - true if the user should be warned if he attempts to open a lot of tabs at - * once (e.g. a large folder of bookmarks), false otherwise - * browser.taskbar.previews.enable - * - true if tabs are to be shown in the Windows 7 taskbar - */ - - /** - * Initialize any platform-specific UI. - */ - init: function () { -#ifdef XP_WIN - const Cc = Components.classes; - const Ci = Components.interfaces; - try { - let sysInfo = Cc["@mozilla.org/system-info;1"]. - getService(Ci.nsIPropertyBag2); - let ver = parseFloat(sysInfo.getProperty("version")); - let showTabsInTaskbar = document.getElementById("showTabsInTaskbar"); - showTabsInTaskbar.hidden = ver < 6.1; - } catch (ex) {} -#endif - // Set the proper value in the newtab drop-down. - gTabsPane.readNewtabUrl(); - }, - - /** - * Pale Moon: synchronize warnOnClose and warnOnCloseOtherTabs - */ - syncWarnOnClose: function() { - var warnOnClosePref = document.getElementById("browser.tabs.warnOnClose"); - var warnOnCloseOtherPref = document.getElementById("browser.tabs.warnOnCloseOtherTabs"); - warnOnCloseOtherPref.value = warnOnClosePref.value; - }, - - /** - * Determines where a link which opens a new window will open. - * - * @returns |true| if such links should be opened in new tabs - */ - readLinkTarget: function() { - var openNewWindow = document.getElementById("browser.link.open_newwindow"); - return openNewWindow.value != 2; - }, - - /** - * Determines where a link which opens a new window will open. - * - * @returns 2 if such links should be opened in new windows, - * 3 if such links should be opened in new tabs - */ - writeLinkTarget: function() { - var linkTargeting = document.getElementById("linkTargeting"); - return linkTargeting.checked ? 3 : 2; - }, - - /** - * Determines the value of the New Tab display drop-down based - * on the value of browser.newtab.url. - */ - readNewtabUrl: function() { - let newtabUrlChoice = document.getElementById("browser.newtab.choice"); - newtabUrlChoice.value = gNewtabUrl.getNewtabChoice(); - if (newtabUrlChoice.value == 0) { - document.getElementById("newtabPageCustom").hidden = false; - } - gNewtabUrl.newtabUrlChoiceIsSet = true; - } -}; diff --git a/components/preferences/tabs.xul b/components/preferences/tabs.xul deleted file mode 100644 index 64529d6..0000000 --- a/components/preferences/tabs.xul +++ /dev/null @@ -1,102 +0,0 @@ -<?xml version="1.0"?> - -# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -<!DOCTYPE overlay [ -<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> -%brandDTD; -<!ENTITY % tabsDTD SYSTEM "chrome://browser/locale/preferences/tabs.dtd"> -%tabsDTD; -]> - -<overlay id="TabsPaneOverlay" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - - <prefpane id="paneTabs" - onpaneload="gTabsPane.init();" - helpTopic="prefs-tabs"> - - <preferences id="tabsPreferences"> - <preference id="browser.link.open_newwindow" name="browser.link.open_newwindow" type="int"/> - <preference id="browser.tabs.autoHide" name="browser.tabs.autoHide" type="bool" inverted="true"/> - <preference id="browser.tabs.loadInBackground" name="browser.tabs.loadInBackground" type="bool" inverted="true"/> - <preference id="browser.tabs.warnOnClose" name="browser.tabs.warnOnClose" type="bool" - onchange="gTabsPane.syncWarnOnClose();"/> - <preference id="browser.tabs.warnOnCloseOtherTabs" name="browser.tabs.warnOnCloseOtherTabs" type="bool"/> - <preference id="browser.tabs.warnOnOpen" name="browser.tabs.warnOnOpen" type="bool"/> - <preference id="browser.sessionstore.restore_on_demand" name="browser.sessionstore.restore_on_demand" type="bool"/> -#ifdef XP_WIN - <preference id="browser.taskbar.previews.enable" name="browser.taskbar.previews.enable" type="bool"/> -#endif - <preference id="browser.tabs.insertRelatedAfterCurrent" name="browser.tabs.insertRelatedAfterCurrent" type="bool"/> - <preference id="browser.search.context.loadInBackground" name="browser.search.context.loadInBackground" type="bool" inverted="true"/> - <preference id="browser.tabs.closeWindowWithLastTab" name="browser.tabs.closeWindowWithLastTab" type="bool"/> - <preference id="browser.ctrlTab.previews" name="browser.ctrlTab.previews" type="bool"/> - - <preference id="browser.newtab.url" name="browser.newtab.url" type="string"/> - <preference id="browser.newtab.myhome" name="browser.newtab.myhome" type="string"/> - <preference id="browser.newtab.choice" name="browser.newtab.choice" type="int"/> - </preferences> - - <script type="application/javascript" src="chrome://browser/content/preferences/tabs.js"/> - - <!-- XXX flex below is a hack because wrapping checkboxes don't reflow - properly; see bug 349098 --> - <vbox id="tabPrefsBox" align="start" flex="1"> - <checkbox id="linkTargeting" label="&newWindowsAsTabs.label;" - accesskey="&newWindowsAsTabs.accesskey;" - preference="browser.link.open_newwindow" - onsyncfrompreference="return gTabsPane.readLinkTarget();" - onsynctopreference="return gTabsPane.writeLinkTarget();"/> - <checkbox id="warnCloseMultiple" label="&warnCloseMultipleTabs.label;" - accesskey="&warnCloseMultipleTabs.accesskey;" - preference="browser.tabs.warnOnClose"/> - <checkbox id="warnOpenMany" label="&warnOpenManyTabs.label;" - accesskey="&warnOpenManyTabs.accesskey;" - preference="browser.tabs.warnOnOpen"/> - <checkbox id="showTabBar" label="&showTabBar.label;" - accesskey="&showTabBar.accesskey;" - preference="browser.tabs.autoHide"/> - <checkbox id="restoreOnDemand" label="&restoreTabsOnDemand.label;" - accesskey="&restoreTabsOnDemand.accesskey;" - preference="browser.sessionstore.restore_on_demand"/> - <checkbox id="switchToNewTabs" label="&switchToNewTabs.label;" - accesskey="&switchToNewTabs.accesskey;" - preference="browser.tabs.loadInBackground"/> -#ifdef XP_WIN - <checkbox id="showTabsInTaskbar" label="&showTabsInTaskbar.label;" - accesskey="&showTabsInTaskbar.accesskey;" - preference="browser.taskbar.previews.enable"/> -#endif -<!-- Pale Moon additions --> - <checkbox id="insertRelatedAfterCurrent" label="&insertRelatedAfterCurrent.label;" - preference="browser.tabs.insertRelatedAfterCurrent"/> - <checkbox id="contextLoadInBackground" label="&contextLoadInBackground.label;" - preference="browser.search.context.loadInBackground"/> - <checkbox id="closeWindowWithLastTab" label="&closeWindowWithLastTab.label;" - preference="browser.tabs.closeWindowWithLastTab"/> - <checkbox id="showTabPreviews" label="&showTabPreviews.label;" - preference="browser.ctrlTab.previews"/> - <hbox align="center"> - <label value="&newtabPage.label;"/> - <menulist - id="newtabPage" - preference="browser.newtab.choice" - oncommand="gNewtabUrl.writeNewtabUrl(event.target.value);"> - <menupopup> - <menuitem label="&newtabPage.custom.label;" value="0" id="newtabPageCustom" hidden="true" /> - <menuitem label="&newtabPage.blank.label;" value="1" /> - <menuitem label="&newtabPage.home.label;" value="2" /> - <menuitem label="&newtabPage.myhome.label;" value="3" /> - <menuitem label="&newtabPage.quickdial.label;" value="4" /> - </menupopup> - </menulist> - </hbox> - </vbox> - - </prefpane> - -</overlay> diff --git a/components/privatebrowsing/content/aboutPrivateBrowsing.xhtml b/components/privatebrowsing/content/aboutPrivateBrowsing.xhtml deleted file mode 100644 index 95744cf..0000000 --- a/components/privatebrowsing/content/aboutPrivateBrowsing.xhtml +++ /dev/null @@ -1,156 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. ---> -<!DOCTYPE html [ - <!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd"> - %htmlDTD; - <!ENTITY % netErrorDTD SYSTEM "chrome://global/locale/netError.dtd"> - %netErrorDTD; - <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd"> - %globalDTD; - <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd"> - %browserDTD; -#ifdef XP_MACOSX - <!ENTITY basePBMenu.label "&fileMenu.label;"> -#else - <!ENTITY basePBMenu.label "<span class='appMenuButton'>&brandShortName;</span><span class='fileMenu'>&fileMenu.label;</span>"> -#endif - <!ENTITY % privatebrowsingpageDTD SYSTEM "chrome://browser/locale/aboutPrivateBrowsing.dtd"> - %privatebrowsingpageDTD; -]> - -<html xmlns="http://www.w3.org/1999/xhtml"> - <head> - <link rel="stylesheet" href="chrome://global/skin/netError.css" type="text/css" media="all"/> - <link rel="stylesheet" href="chrome://browser/skin/aboutPrivateBrowsing.css" type="text/css" media="all"/> - <style type="text/css"><![CDATA[ - body.normal .showPrivate, - body.private .showNormal { - display: none; - } - body.appMenuButtonVisible .fileMenu { - display: none; - } - body.appMenuButtonInvisible .appMenuButton { - display: none; - } - ]]></style> - <script type="application/javascript;version=1.7"><![CDATA[ - const Cc = Components.classes; - const Ci = Components.interfaces; - - Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); - - if (!PrivateBrowsingUtils.isWindowPrivate(window)) { - document.title = "]]>&privatebrowsingpage.title.normal;<![CDATA["; - setFavIcon("chrome://global/skin/icons/question-16.png"); - } else { -#ifndef XP_MACOSX - document.title = "]]>&privatebrowsingpage.title;<![CDATA["; -#endif - setFavIcon("chrome://browser/skin/Privacy-16.png"); - } - - var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShellTreeItem) - .rootTreeItem - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindow); - - // Focus the location bar - mainWindow.focusAndSelectUrlBar(); - - function setFavIcon(url) { - var icon = document.createElement("link"); - icon.setAttribute("rel", "icon"); - icon.setAttribute("type", "image/png"); - icon.setAttribute("href", url); - var head = document.getElementsByTagName("head")[0]; - head.insertBefore(icon, head.firstChild); - } - - document.addEventListener("DOMContentLoaded", function () { - if (!PrivateBrowsingUtils.isWindowPrivate(window)) { - document.body.setAttribute("class", "normal"); - } - - // Set up the help link - let moreInfoURL = Cc["@mozilla.org/toolkit/URLFormatterService;1"]. - getService(Ci.nsIURLFormatter). - formatURLPref("app.support.baseURL"); - let moreInfoLink = document.getElementById("moreInfoLink"); - if (moreInfoLink) - moreInfoLink.setAttribute("href", moreInfoURL + "private-browsing"); - - // Show the correct menu structure based on whether the App Menu button is - // shown or not. - var menuBar = mainWindow.document.getElementById("toolbar-menubar"); - var appMenuButtonIsVisible = menuBar.getAttribute("autohide") == "true"; - document.body.classList.add(appMenuButtonIsVisible ? "appMenuButtonVisible" : - "appMenuButtonInvisible"); - }, false); - - function openPrivateWindow() { - mainWindow.OpenBrowserWindow({private: true}); - } - ]]></script> - </head> - - <body dir="&locale.dir;" - class="private"> - - <!-- PAGE CONTAINER (for styling purposes only) --> - <div id="errorPageContainer"> - - <!-- Error Title --> - <div id="errorTitle"> - <h1 id="errorTitleText" class="showPrivate">&privatebrowsingpage.title;</h1> - <h1 id="errorTitleTextNormal" class="showNormal">&privatebrowsingpage.title.normal;</h1> - </div> - - <!-- LONG CONTENT (the section most likely to require scrolling) --> - <div id="errorLongContent"> - - <!-- Short Description --> - <div id="errorShortDesc"> - <p id="errorShortDescText" class="showPrivate">&privatebrowsingpage.perwindow.issueDesc;</p> - <p id="errorShortDescTextNormal" class="showNormal">&privatebrowsingpage.perwindow.issueDesc.normal;</p> - </div> - - <!-- Long Description --> - <div id="errorLongDesc"> - <p id="errorLongDescText">&privatebrowsingpage.perwindow.description;</p> - </div> - - <!-- Start Private Browsing --> - <div id="startPrivateBrowsingDesc" class="showNormal"> - <button xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - id="startPrivateBrowsing" label="&privatebrowsingpage.openPrivateWindow.label;" - accesskey="&privatebrowsingpage.openPrivateWindow.accesskey;" - oncommand="openPrivateWindow();"/> - </div> - - <!-- Footer --> - <div id="footerDesc"> - <p id="footerText" class="showPrivate">&privatebrowsingpage.howToStop3;</p> - <p id="footerTextNormal" class="showNormal">&privatebrowsingpage.howToStart3;</p> - </div> - - <!-- More Info --> - <div id="moreInfo" class="showPrivate"> - <p id="moreInfoText"> - &privatebrowsingpage.moreInfo; - </p> - <p id="moreInfoLinkContainer"> - <a id="moreInfoLink" target="_blank">&privatebrowsingpage.learnMore;</a> - </p> - </div> - </div> - </div> - - </body> -</html> diff --git a/components/privatebrowsing/jar.mn b/components/privatebrowsing/jar.mn deleted file mode 100644 index 75e985c..0000000 --- a/components/privatebrowsing/jar.mn +++ /dev/null @@ -1,6 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -browser.jar: -* content/browser/aboutPrivateBrowsing.xhtml (content/aboutPrivateBrowsing.xhtml) diff --git a/components/privatebrowsing/moz.build b/components/privatebrowsing/moz.build deleted file mode 100644 index c97072b..0000000 --- a/components/privatebrowsing/moz.build +++ /dev/null @@ -1,7 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -JAR_MANIFESTS += ['jar.mn']
\ No newline at end of file diff --git a/components/search/content/engineManager.js b/components/search/content/engineManager.js deleted file mode 100644 index 993d48b..0000000 --- a/components/search/content/engineManager.js +++ /dev/null @@ -1,492 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); -Components.utils.import("resource://gre/modules/Services.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", - "resource://gre/modules/PlacesUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Task", - "resource://gre/modules/Task.jsm"); - -const Ci = Components.interfaces; -const Cc = Components.classes; - -const ENGINE_FLAVOR = "text/x-moz-search-engine"; - -const BROWSER_SUGGEST_PREF = "browser.search.suggest.enabled"; - -var gEngineView = null; - -var gEngineManagerDialog = { - init: function engineManager_init() { - gEngineView = new EngineView(new EngineStore()); - - var suggestEnabled = Services.prefs.getBoolPref(BROWSER_SUGGEST_PREF); - document.getElementById("enableSuggest").checked = suggestEnabled; - - var tree = document.getElementById("engineList"); - tree.view = gEngineView; - - Services.obs.addObserver(this, "browser-search-engine-modified", false); - }, - - destroy: function engineManager_destroy() { - // Remove the observer - Services.obs.removeObserver(this, "browser-search-engine-modified"); - }, - - observe: function engineManager_observe(aEngine, aTopic, aVerb) { - if (aTopic == "browser-search-engine-modified") { - aEngine.QueryInterface(Ci.nsISearchEngine); - switch (aVerb) { - case "engine-added": - gEngineView._engineStore.addEngine(aEngine); - gEngineView.rowCountChanged(gEngineView.lastIndex, 1); - break; - case "engine-changed": - gEngineView._engineStore.reloadIcons(); - gEngineView.invalidate(); - break; - case "engine-removed": - case "engine-current": - case "engine-default": - // Not relevant - break; - } - } - }, - - onOK: function engineManager_onOK() { - // Set the preference - var newSuggestEnabled = document.getElementById("enableSuggest").checked; - Services.prefs.setBoolPref(BROWSER_SUGGEST_PREF, newSuggestEnabled); - - // Commit the changes - gEngineView._engineStore.commit(); - }, - - onRestoreDefaults: function engineManager_onRestoreDefaults() { - var num = gEngineView._engineStore.restoreDefaultEngines(); - gEngineView.rowCountChanged(0, num); - gEngineView.invalidate(); - }, - - showRestoreDefaults: function engineManager_showRestoreDefaults(val) { - document.documentElement.getButton("extra2").disabled = !val; - }, - - loadAddEngines: function engineManager_loadAddEngines() { - this.onOK(); - window.opener.BrowserSearch.loadAddEngines(); - window.close(); - }, - - remove: function engineManager_remove() { - gEngineView._engineStore.removeEngine(gEngineView.selectedEngine); - var index = gEngineView.selectedIndex; - gEngineView.rowCountChanged(index, -1); - gEngineView.invalidate(); - gEngineView.selection.select(Math.min(index, gEngineView.lastIndex)); - gEngineView.ensureRowIsVisible(gEngineView.currentIndex); - document.getElementById("engineList").focus(); - }, - - /** - * Moves the selected engine either up or down in the engine list - * @param aDir - * -1 to move the selected engine down, +1 to move it up. - */ - bump: function engineManager_move(aDir) { - var selectedEngine = gEngineView.selectedEngine; - var newIndex = gEngineView.selectedIndex - aDir; - - gEngineView._engineStore.moveEngine(selectedEngine, newIndex); - - gEngineView.invalidate(); - gEngineView.selection.select(newIndex); - gEngineView.ensureRowIsVisible(newIndex); - this.showRestoreDefaults(true); - document.getElementById("engineList").focus(); - }, - - editKeyword: Task.async(function* engineManager_editKeyword() { - var selectedEngine = gEngineView.selectedEngine; - if (!selectedEngine) - return; - - var alias = { value: selectedEngine.alias }; - var strings = document.getElementById("engineManagerBundle"); - var title = strings.getString("editTitle"); - var msg = strings.getFormattedString("editMsg", [selectedEngine.name]); - - while (Services.prompt.prompt(window, title, msg, alias, null, {})) { - var bduplicate = false; - var eduplicate = false; - var dupName = ""; - - if (alias.value != "") { - // Check for duplicates in Places keywords. - bduplicate = !!(yield PlacesUtils.keywords.fetch(alias.value)); - - // Check for duplicates in changes we haven't committed yet - let engines = gEngineView._engineStore.engines; - for each (let engine in engines) { - if (engine.alias == alias.value && - engine.name != selectedEngine.name) { - eduplicate = true; - dupName = engine.name; - break; - } - } - } - - // Notify the user if they have chosen an existing engine/bookmark keyword - if (eduplicate || bduplicate) { - var dtitle = strings.getString("duplicateTitle"); - var bmsg = strings.getString("duplicateBookmarkMsg"); - var emsg = strings.getFormattedString("duplicateEngineMsg", [dupName]); - - Services.prompt.alert(window, dtitle, eduplicate ? emsg : bmsg); - } else { - gEngineView._engineStore.changeEngine(selectedEngine, "alias", - alias.value); - gEngineView.invalidate(); - break; - } - } - }), - - onSelect: function engineManager_onSelect() { - // Buttons only work if an engine is selected and it's not the last engine, - // the latter is true when the selected is first and last at the same time. - var lastSelected = (gEngineView.selectedIndex == gEngineView.lastIndex); - var firstSelected = (gEngineView.selectedIndex == 0); - var noSelection = (gEngineView.selectedIndex == -1); - - document.getElementById("cmd_remove") - .setAttribute("disabled", noSelection || - (firstSelected && lastSelected)); - - document.getElementById("cmd_moveup") - .setAttribute("disabled", noSelection || firstSelected); - - document.getElementById("cmd_movedown") - .setAttribute("disabled", noSelection || lastSelected); - - document.getElementById("cmd_editkeyword") - .setAttribute("disabled", noSelection); - } -}; - -function onDragEngineStart(event) { - var selectedIndex = gEngineView.selectedIndex; - if (selectedIndex >= 0) { - event.dataTransfer.setData(ENGINE_FLAVOR, selectedIndex.toString()); - event.dataTransfer.effectAllowed = "move"; - } -} - -// "Operation" objects -function EngineMoveOp(aEngineClone, aNewIndex) { - if (!aEngineClone) - throw new Error("bad args to new EngineMoveOp!"); - this._engine = aEngineClone.originalEngine; - this._newIndex = aNewIndex; -} -EngineMoveOp.prototype = { - _engine: null, - _newIndex: null, - commit: function EMO_commit() { - Services.search.moveEngine(this._engine, this._newIndex); - } -} - -function EngineRemoveOp(aEngineClone) { - if (!aEngineClone) - throw new Error("bad args to new EngineRemoveOp!"); - this._engine = aEngineClone.originalEngine; -} -EngineRemoveOp.prototype = { - _engine: null, - commit: function ERO_commit() { - Services.search.removeEngine(this._engine); - } -} - -function EngineUnhideOp(aEngineClone, aNewIndex) { - if (!aEngineClone) - throw new Error("bad args to new EngineUnhideOp!"); - this._engine = aEngineClone.originalEngine; - this._newIndex = aNewIndex; -} -EngineUnhideOp.prototype = { - _engine: null, - _newIndex: null, - commit: function EUO_commit() { - this._engine.hidden = false; - Services.search.moveEngine(this._engine, this._newIndex); - } -} - -function EngineChangeOp(aEngineClone, aProp, aValue) { - if (!aEngineClone) - throw new Error("bad args to new EngineChangeOp!"); - - this._engine = aEngineClone.originalEngine; - this._prop = aProp; - this._newValue = aValue; -} -EngineChangeOp.prototype = { - _engine: null, - _prop: null, - _newValue: null, - commit: function ECO_commit() { - this._engine[this._prop] = this._newValue; - } -} - -function EngineStore() { - this._engines = Services.search.getVisibleEngines().map(this._cloneEngine); - this._defaultEngines = Services.search.getDefaultEngines().map(this._cloneEngine); - - this._ops = []; - - // check if we need to disable the restore defaults button - var someHidden = this._defaultEngines.some(function (e) e.hidden); - gEngineManagerDialog.showRestoreDefaults(someHidden); -} -EngineStore.prototype = { - _engines: null, - _defaultEngines: null, - _ops: null, - - get engines() { - return this._engines; - }, - set engines(val) { - this._engines = val; - return val; - }, - - _getIndexForEngine: function ES_getIndexForEngine(aEngine) { - return this._engines.indexOf(aEngine); - }, - - _getEngineByName: function ES_getEngineByName(aName) { - for each (var engine in this._engines) - if (engine.name == aName) - return engine; - - return null; - }, - - _cloneEngine: function ES_cloneEngine(aEngine) { - var clonedObj={}; - for (var i in aEngine) - clonedObj[i] = aEngine[i]; - clonedObj.originalEngine = aEngine; - return clonedObj; - }, - - // Callback for Array's some(). A thisObj must be passed to some() - _isSameEngine: function ES_isSameEngine(aEngineClone) { - return aEngineClone.originalEngine == this.originalEngine; - }, - - commit: function ES_commit() { - var currentEngine = this._cloneEngine(Services.search.currentEngine); - for (var i = 0; i < this._ops.length; i++) - this._ops[i].commit(); - - // Restore currentEngine if it is a default engine that is still visible. - // Needed if the user deletes currentEngine and then restores it. - if (this._defaultEngines.some(this._isSameEngine, currentEngine) && - !currentEngine.originalEngine.hidden) - Services.search.currentEngine = currentEngine.originalEngine; - }, - - addEngine: function ES_addEngine(aEngine) { - this._engines.push(this._cloneEngine(aEngine)); - }, - - moveEngine: function ES_moveEngine(aEngine, aNewIndex) { - if (aNewIndex < 0 || aNewIndex > this._engines.length - 1) - throw new Error("ES_moveEngine: invalid aNewIndex!"); - var index = this._getIndexForEngine(aEngine); - if (index == -1) - throw new Error("ES_moveEngine: invalid engine?"); - - if (index == aNewIndex) - return; // nothing to do - - // Move the engine in our internal store - var removedEngine = this._engines.splice(index, 1)[0]; - this._engines.splice(aNewIndex, 0, removedEngine); - - this._ops.push(new EngineMoveOp(aEngine, aNewIndex)); - }, - - removeEngine: function ES_removeEngine(aEngine) { - var index = this._getIndexForEngine(aEngine); - if (index == -1) - throw new Error("invalid engine?"); - - this._engines.splice(index, 1); - this._ops.push(new EngineRemoveOp(aEngine)); - if (this._defaultEngines.some(this._isSameEngine, aEngine)) - gEngineManagerDialog.showRestoreDefaults(true); - }, - - restoreDefaultEngines: function ES_restoreDefaultEngines() { - var added = 0; - - for (var i = 0; i < this._defaultEngines.length; ++i) { - var e = this._defaultEngines[i]; - - // If the engine is already in the list, just move it. - if (this._engines.some(this._isSameEngine, e)) { - this.moveEngine(this._getEngineByName(e.name), i); - } else { - // Otherwise, add it back to our internal store - this._engines.splice(i, 0, e); - this._ops.push(new EngineUnhideOp(e, i)); - added++; - } - } - gEngineManagerDialog.showRestoreDefaults(false); - return added; - }, - - changeEngine: function ES_changeEngine(aEngine, aProp, aNewValue) { - var index = this._getIndexForEngine(aEngine); - if (index == -1) - throw new Error("invalid engine?"); - - this._engines[index][aProp] = aNewValue; - this._ops.push(new EngineChangeOp(aEngine, aProp, aNewValue)); - }, - - reloadIcons: function ES_reloadIcons() { - this._engines.forEach(function (e) { - e.uri = e.originalEngine.uri; - }); - } -} - -function EngineView(aEngineStore) { - this._engineStore = aEngineStore; -} -EngineView.prototype = { - _engineStore: null, - tree: null, - - get lastIndex() { - return this.rowCount - 1; - }, - get selectedIndex() { - var seln = this.selection; - if (seln.getRangeCount() > 0) { - var min = {}; - seln.getRangeAt(0, min, {}); - return min.value; - } - return -1; - }, - get selectedEngine() { - return this._engineStore.engines[this.selectedIndex]; - }, - - // Helpers - rowCountChanged: function (index, count) { - this.tree.rowCountChanged(index, count); - }, - - invalidate: function () { - this.tree.invalidate(); - }, - - ensureRowIsVisible: function (index) { - this.tree.ensureRowIsVisible(index); - }, - - getSourceIndexFromDrag: function (dataTransfer) { - return parseInt(dataTransfer.getData(ENGINE_FLAVOR)); - }, - - // nsITreeView - get rowCount() { - return this._engineStore.engines.length; - }, - - getImageSrc: function(index, column) { - if (column.id == "engineName" && this._engineStore.engines[index].iconURI) - return this._engineStore.engines[index].iconURI.spec; - return ""; - }, - - getCellText: function(index, column) { - if (column.id == "engineName") - return this._engineStore.engines[index].name; - else if (column.id == "engineKeyword") - return this._engineStore.engines[index].alias; - return ""; - }, - - setTree: function(tree) { - this.tree = tree; - }, - - canDrop: function(targetIndex, orientation, dataTransfer) { - var sourceIndex = this.getSourceIndexFromDrag(dataTransfer); - return (sourceIndex != -1 && - sourceIndex != targetIndex && - sourceIndex != targetIndex + orientation); - }, - - drop: function(dropIndex, orientation, dataTransfer) { - var sourceIndex = this.getSourceIndexFromDrag(dataTransfer); - var sourceEngine = this._engineStore.engines[sourceIndex]; - - if (dropIndex > sourceIndex) { - if (orientation == Ci.nsITreeView.DROP_BEFORE) - dropIndex--; - } else { - if (orientation == Ci.nsITreeView.DROP_AFTER) - dropIndex++; - } - - this._engineStore.moveEngine(sourceEngine, dropIndex); - gEngineManagerDialog.showRestoreDefaults(true); - - // Redraw, and adjust selection - this.invalidate(); - this.selection.select(dropIndex); - }, - - selection: null, - getRowProperties: function(index) { return ""; }, - getCellProperties: function(index, column) { return ""; }, - getColumnProperties: function(column) { return ""; }, - isContainer: function(index) { return false; }, - isContainerOpen: function(index) { return false; }, - isContainerEmpty: function(index) { return false; }, - isSeparator: function(index) { return false; }, - isSorted: function(index) { return false; }, - getParentIndex: function(index) { return -1; }, - hasNextSibling: function(parentIndex, index) { return false; }, - getLevel: function(index) { return 0; }, - getProgressMode: function(index, column) { }, - getCellValue: function(index, column) { }, - toggleOpenState: function(index) { }, - cycleHeader: function(column) { }, - selectionChanged: function() { }, - cycleCell: function(row, column) { }, - isEditable: function(index, column) { return false; }, - isSelectable: function(index, column) { return false; }, - setCellValue: function(index, column, value) { }, - setCellText: function(index, column, value) { }, - performAction: function(action) { }, - performActionOnRow: function(action, index) { }, - performActionOnCell: function(action, index, column) { } -}; diff --git a/components/search/content/engineManager.xul b/components/search/content/engineManager.xul deleted file mode 100644 index 1152ef8..0000000 --- a/components/search/content/engineManager.xul +++ /dev/null @@ -1,93 +0,0 @@ -<?xml version="1.0"?> -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<?xml-stylesheet href="chrome://global/skin/"?> -<?xml-stylesheet href="chrome://browser/skin/engineManager.css"?> - -<!DOCTYPE dialog SYSTEM "chrome://browser/locale/engineManager.dtd"> - -<dialog id="engineManager" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - buttons="accept,cancel,extra2" - buttonlabelextra2="&restoreDefaults.label;" - buttonaccesskeyextra2="&restoreDefaults.accesskey;" - onload="gEngineManagerDialog.init();" - onunload="gEngineManagerDialog.destroy();" - ondialogaccept="gEngineManagerDialog.onOK();" - ondialogextra2="gEngineManagerDialog.onRestoreDefaults();" - title="&engineManager.title;" - style="&engineManager.style;" - persist="screenX screenY width height" - windowtype="Browser:SearchManager"> - - <script type="application/javascript" - src="chrome://browser/content/search/engineManager.js"/> - - <commandset id="engineManagerCommandSet"> - <command id="cmd_remove" - oncommand="gEngineManagerDialog.remove();" - disabled="true"/> - <command id="cmd_moveup" - oncommand="gEngineManagerDialog.bump(1);" - disabled="true"/> - <command id="cmd_movedown" - oncommand="gEngineManagerDialog.bump(-1);" - disabled="true"/> - <command id="cmd_editkeyword" - oncommand="gEngineManagerDialog.editKeyword().catch(Components.utils.reportError);" - disabled="true"/> - </commandset> - - <keyset id="engineManagerKeyset"> - <key id="delete" keycode="VK_DELETE" command="cmd_remove"/> - </keyset> - - <stringbundleset id="engineManagerBundleset"> - <stringbundle id="engineManagerBundle" src="chrome://browser/locale/engineManager.properties"/> - </stringbundleset> - - <description>&engineManager.intro;</description> - <separator class="thin"/> - <hbox flex="1"> - <tree id="engineList" flex="1" rows="10" hidecolumnpicker="true" - seltype="single" onselect="gEngineManagerDialog.onSelect();"> - <treechildren id="engineChildren" flex="1" - ondragstart="onDragEngineStart(event);"/> - <treecols> - <treecol id="engineName" flex="4" label="&columnLabel.name;"/> - <treecol id="engineKeyword" flex="1" label="&columnLabel.keyword;"/> - </treecols> - </tree> - <vbox> - <spacer flex="1"/> - <button id="edit" - label="&edit.label;" - accesskey="&edit.accesskey;" - command="cmd_editkeyword"/> - <button id="up" - label="&up.label;" - accesskey="&up.accesskey;" - command="cmd_moveup"/> - <button id="down" - label="&dn.label;" - accesskey="&dn.accesskey;" - command="cmd_movedown"/> - <spacer flex="1"/> - <button id="remove" - label="&remove.label;" - accesskey="&remove.accesskey;" - command="cmd_remove"/> - </vbox> - </hbox> - <hbox> - <checkbox id="enableSuggest" - label="&enableSuggest.label;" - accesskey="&enableSuggest.accesskey;"/> - </hbox> - <hbox> - <label id="addEngines" class="text-link" value="&addEngine.label;" - onclick="if (event.button == 0) { gEngineManagerDialog.loadAddEngines(); }"/> - </hbox> -</dialog> diff --git a/components/search/content/search.xml b/components/search/content/search.xml deleted file mode 100644 index 0c33b15..0000000 --- a/components/search/content/search.xml +++ /dev/null @@ -1,837 +0,0 @@ -<?xml version="1.0"?> -# -*- Mode: HTML -*- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -<!DOCTYPE bindings [ -<!ENTITY % searchBarDTD SYSTEM "chrome://browser/locale/searchbar.dtd" > -%searchBarDTD; -<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd"> -%browserDTD; -]> - -<bindings id="SearchBindings" - xmlns="http://www.mozilla.org/xbl" - xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:xbl="http://www.mozilla.org/xbl"> - - <binding id="searchbar"> - <resources> - <stylesheet src="chrome://browser/content/search/searchbarBindings.css"/> - <stylesheet src="chrome://browser/skin/searchbar.css"/> - </resources> - <content> - <xul:stringbundle src="chrome://browser/locale/search.properties" - anonid="searchbar-stringbundle"/> - - <xul:textbox class="searchbar-textbox" - anonid="searchbar-textbox" - type="autocomplete" - flex="1" - autocompletepopup="PopupAutoComplete" - autocompletesearch="search-autocomplete" - autocompletesearchparam="searchbar-history" - timeout="250" - maxrows="10" - completeselectedindex="true" - showcommentcolumn="true" - tabscrolling="true" - xbl:inherits="disabled,disableautocomplete,searchengine,src,newlines"> - <xul:box> - <xul:button class="searchbar-engine-button" - type="menu" - anonid="searchbar-engine-button"> - <xul:image class="searchbar-engine-image" xbl:inherits="src"/> - <xul:image class="searchbar-dropmarker-image"/> - <xul:menupopup class="searchbar-popup" - anonid="searchbar-popup"> - <xul:menuseparator/> - <xul:menuitem class="open-engine-manager" - anonid="open-engine-manager" - label="&cmd_engineManager.label;" - oncommand="openManager(event);"/> - </xul:menupopup> - </xul:button> - </xul:box> - <xul:hbox class="search-go-container"> - <xul:image class="search-go-button" - anonid="search-go-button" - onclick="handleSearchCommand(event);" - tooltiptext="&searchEndCap.label;"/> - </xul:hbox> - </xul:textbox> - </content> - - <implementation implements="nsIObserver"> - <constructor><![CDATA[ - if (this.parentNode.parentNode.localName == "toolbarpaletteitem") - return; - // Make sure we rebuild the popup in onpopupshowing - this._needToBuildPopup = true; - - var os = - Components.classes["@mozilla.org/observer-service;1"] - .getService(Components.interfaces.nsIObserverService); - os.addObserver(this, "browser-search-engine-modified", false); - - this._initialized = true; - - this.searchService.init((function search_init_cb(aStatus) { - // Bail out if the binding has been destroyed - if (!this._initialized) - return; - - if (Components.isSuccessCode(aStatus)) { - // Refresh the display (updating icon, etc) - this.updateDisplay(); - } else { - Components.utils.reportError("Cannot initialize search service, bailing out: " + aStatus); - } - }).bind(this)); - ]]></constructor> - - <destructor><![CDATA[ - if (this._initialized) { - this._initialized = false; - - var os = Components.classes["@mozilla.org/observer-service;1"] - .getService(Components.interfaces.nsIObserverService); - os.removeObserver(this, "browser-search-engine-modified"); - } - - // Make sure to break the cycle from _textbox to us. Otherwise we leak - // the world. But make sure it's actually pointing to us. - if (this._textbox.mController.input == this) - this._textbox.mController.input = null; - ]]></destructor> - - <field name="_stringBundle">document.getAnonymousElementByAttribute(this, - "anonid", "searchbar-stringbundle");</field> - <field name="_textbox">document.getAnonymousElementByAttribute(this, - "anonid", "searchbar-textbox");</field> - <field name="_popup">document.getAnonymousElementByAttribute(this, - "anonid", "searchbar-popup");</field> - <field name="_ss">null</field> - <field name="_engines">null</field> - <field name="FormHistory" readonly="true"> - (Components.utils.import("resource://gre/modules/FormHistory.jsm", {})).FormHistory; - </field> - - <property name="engines" readonly="true"> - <getter><![CDATA[ - if (!this._engines) - this._engines = this.searchService.getVisibleEngines(); - return this._engines; - ]]></getter> - </property> - - <field name="searchButton">document.getAnonymousElementByAttribute(this, - "anonid", "searchbar-engine-button");</field> - - <property name="currentEngine"> - <setter><![CDATA[ - let ss = this.searchService; - ss.defaultEngine = ss.currentEngine = val; - return val; - ]]></setter> - <getter><![CDATA[ - var currentEngine = this.searchService.currentEngine; - // Return a dummy engine if there is no currentEngine - return currentEngine || {name: "", uri: null}; - ]]></getter> - </property> - - <!-- textbox is used by sanitize.js to clear the undo history when - clearing form information. --> - <property name="textbox" readonly="true" - onget="return this._textbox;"/> - - <property name="searchService" readonly="true"> - <getter><![CDATA[ - if (!this._ss) { - const nsIBSS = Components.interfaces.nsIBrowserSearchService; - this._ss = - Components.classes["@mozilla.org/browser/search-service;1"] - .getService(nsIBSS); - } - return this._ss; - ]]></getter> - </property> - - <property name="value" onget="return this._textbox.value;" - onset="return this._textbox.value = val;"/> - - <method name="focus"> - <body><![CDATA[ - this._textbox.focus(); - ]]></body> - </method> - - <method name="select"> - <body><![CDATA[ - this._textbox.select(); - ]]></body> - </method> - - <method name="observe"> - <parameter name="aEngine"/> - <parameter name="aTopic"/> - <parameter name="aVerb"/> - <body><![CDATA[ - if (aTopic == "browser-search-engine-modified") { - switch (aVerb) { - case "engine-removed": - this.offerNewEngine(aEngine); - break; - case "engine-added": - this.hideNewEngine(aEngine); - break; - case "engine-current": - // The current engine was changed. Rebuilding the menu appears to - // confuse its idea of whether it should be open when it's just - // been clicked, so we force it to close now. - this._popup.hidePopup(); - break; - case "engine-changed": - // An engine was removed (or hidden) or added, or an icon was - // changed. Do nothing special. - } - - // Make sure the engine list is refetched next time it's needed - this._engines = null; - - // Rebuild the popup and update the display after any modification. - this.rebuildPopup(); - this.updateDisplay(); - } - ]]></body> - </method> - - <!-- There are two seaprate lists of search engines, whose uses intersect - in this file. The search service (nsIBrowserSearchService and - nsSearchService.js) maintains a list of Engine objects which is used to - populate the searchbox list of available engines and to perform queries. - That list is accessed here via this.SearchService, and it's that sort of - Engine that is passed to this binding's observer as aEngine. - - In addition, browser.js fills two lists of autodetected search engines - (browser.engines and browser.hiddenEngines) as properties of - mCurrentBrowser. Those lists contain unnamed JS objects of the form - { uri:, title:, icon: }, and that's what the searchbar uses to determine - whether to show any "Add <EngineName>" menu items in the drop-down. - - The two types of engines are currently related by their identifying - titles (the Engine object's 'name'), although that may change; see bug - 335102. --> - - <!-- If the engine that was just removed from the searchbox list was - autodetected on this page, move it to each browser's active list so it - will be offered to be added again. --> - <method name="offerNewEngine"> - <parameter name="aEngine"/> - <body><![CDATA[ - var allbrowsers = getBrowser().mPanelContainer.childNodes; - for (var tab = 0; tab < allbrowsers.length; tab++) { - var browser = getBrowser().getBrowserAtIndex(tab); - if (browser.hiddenEngines) { - // XXX This will need to be changed when engines are identified by - // URL rather than title; see bug 335102. - var removeTitle = aEngine.wrappedJSObject.name; - for (var i = 0; i < browser.hiddenEngines.length; i++) { - if (browser.hiddenEngines[i].title == removeTitle) { - if (!browser.engines) - browser.engines = []; - browser.engines.push(browser.hiddenEngines[i]); - browser.hiddenEngines.splice(i, 1); - break; - } - } - } - } - ]]></body> - </method> - - <!-- If the engine that was just added to the searchbox list was - autodetected on this page, move it to each browser's hidden list so it is - no longer offered to be added. --> - <method name="hideNewEngine"> - <parameter name="aEngine"/> - <body><![CDATA[ - var allbrowsers = getBrowser().mPanelContainer.childNodes; - for (var tab = 0; tab < allbrowsers.length; tab++) { - var browser = getBrowser().getBrowserAtIndex(tab); - if (browser.engines) { - // XXX This will need to be changed when engines are identified by - // URL rather than title; see bug 335102. - var removeTitle = aEngine.wrappedJSObject.name; - for (var i = 0; i < browser.engines.length; i++) { - if (browser.engines[i].title == removeTitle) { - if (!browser.hiddenEngines) - browser.hiddenEngines = []; - browser.hiddenEngines.push(browser.engines[i]); - browser.engines.splice(i, 1); - break; - } - } - } - } - ]]></body> - </method> - - <method name="setIcon"> - <parameter name="element"/> - <parameter name="uri"/> - <body><![CDATA[ - element.setAttribute("src", uri); - ]]></body> - </method> - - <method name="updateDisplay"> - <body><![CDATA[ - var uri = this.currentEngine.iconURI; - this.setIcon(this, uri ? uri.spec : ""); - - var name = this.currentEngine.name; - var text = this._stringBundle.getFormattedString("searchtip", [name]); - this._textbox.placeholder = name; - this._textbox.label = text; - this._textbox.tooltipText = text; - ]]></body> - </method> - - <!-- Rebuilds the dynamic portion of the popup menu (i.e., the menu items - for new search engines that can be added to the available list). This - is called each time the popup is shown. - --> - <method name="rebuildPopupDynamic"> - <body><![CDATA[ - // We might not have added the main popup items yet, do that first - // if needed. - if (this._needToBuildPopup) - this.rebuildPopup(); - - var popup = this._popup; - // Clear any addengine menuitems, including addengine-item entries and - // the addengine-separator. Work backward to avoid invalidating the - // indexes as items are removed. - var items = popup.childNodes; - for (var i = items.length - 1; i >= 0; i--) { - if (items[i].classList.contains("addengine-item") || - items[i].classList.contains("addengine-separator")) - popup.removeChild(items[i]); - } - - var addengines = getBrowser().mCurrentBrowser.engines; - if (addengines && addengines.length > 0) { - const kXULNS = - "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; - - // Find the (first) separator in the remaining menu, or the first item - // if no separators are present. - var insertLocation = popup.firstChild; - while (insertLocation.nextSibling && - insertLocation.localName != "menuseparator") { - insertLocation = insertLocation.nextSibling; - } - if (insertLocation.localName != "menuseparator") - insertLocation = popup.firstChild; - - var separator = document.createElementNS(kXULNS, "menuseparator"); - separator.setAttribute("class", "addengine-separator"); - popup.insertBefore(separator, insertLocation); - - // Insert the "add this engine" items. - for (var i = 0; i < addengines.length; i++) { - var menuitem = document.createElement("menuitem"); - var engineInfo = addengines[i]; - var labelStr = - this._stringBundle.getFormattedString("cmd_addFoundEngine", - [engineInfo.title]); - menuitem = document.createElementNS(kXULNS, "menuitem"); - menuitem.setAttribute("class", "menuitem-iconic addengine-item"); - menuitem.setAttribute("label", labelStr); - menuitem.setAttribute("tooltiptext", engineInfo.uri); - menuitem.setAttribute("uri", engineInfo.uri); - if (engineInfo.icon) - this.setIcon(menuitem, engineInfo.icon); - menuitem.setAttribute("title", engineInfo.title); - popup.insertBefore(menuitem, insertLocation); - } - } - ]]></body> - </method> - - <!-- Rebuilds the list of visible search engines in the menu. Does not remove - or update any dynamic entries (i.e., "Add this engine" items) nor the - Manage Engines item. This is called by the observer when the list of - visible engines, or the currently selected engine, has changed. - --> - <method name="rebuildPopup"> - <body><![CDATA[ - var popup = this._popup; - - // Clear the popup, down to the first separator - while (popup.firstChild && popup.firstChild.localName != "menuseparator") - popup.removeChild(popup.firstChild); - - const kXULNS = - "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; - - var engines = this.engines; - for (var i = engines.length - 1; i >= 0; --i) { - var menuitem = document.createElementNS(kXULNS, "menuitem"); - var name = engines[i].name; - menuitem.setAttribute("label", name); - menuitem.setAttribute("id", name); - menuitem.setAttribute("class", "menuitem-iconic searchbar-engine-menuitem menuitem-with-favicon"); - // Since this menu is rebuilt by the observer method whenever a new - // engine is selected, the "selected" attribute does not need to be - // explicitly cleared anywhere. - if (engines[i] == this.currentEngine) - menuitem.setAttribute("selected", "true"); - var tooltip = this._stringBundle.getFormattedString("searchtip", [name]); - menuitem.setAttribute("tooltiptext", tooltip); - if (engines[i].iconURI) - this.setIcon(menuitem, engines[i].iconURI.spec); - popup.insertBefore(menuitem, popup.firstChild); - menuitem.engine = engines[i]; - } - - this._needToBuildPopup = false; - ]]></body> - </method> - - <method name="openManager"> - <parameter name="aEvent"/> - <body><![CDATA[ - var wm = - Components.classes["@mozilla.org/appshell/window-mediator;1"] - .getService(Components.interfaces.nsIWindowMediator); - - var window = wm.getMostRecentWindow("Browser:SearchManager"); - if (window) - window.focus() - else { - setTimeout(function () { - openDialog("chrome://browser/content/search/engineManager.xul", - "_blank", "chrome,dialog,modal,centerscreen,resizable"); - }, 0); - } - ]]></body> - </method> - - <method name="selectEngine"> - <parameter name="aEvent"/> - <parameter name="isNextEngine"/> - <body><![CDATA[ - // Find the new index - var newIndex = this.engines.indexOf(this.currentEngine); - newIndex += isNextEngine ? 1 : -1; - - if (newIndex >= 0 && newIndex < this.engines.length) { - this.currentEngine = this.engines[newIndex]; - } - - aEvent.preventDefault(); - aEvent.stopPropagation(); - ]]></body> - </method> - - <method name="handleSearchCommand"> - <parameter name="aEvent"/> - <body><![CDATA[ - var textBox = this._textbox; - var textValue = textBox.value; - - var where = "current"; - if (aEvent && aEvent.originalTarget.getAttribute("anonid") == "search-go-button") { - if (aEvent.button == 2) - return; - where = whereToOpenLink(aEvent, false, true); - } - else { - var newTabPref = textBox._prefBranch.getBoolPref("browser.search.openintab"); - if ((aEvent && aEvent.altKey) ^ newTabPref) - where = "tab"; - } - - this.doSearch(textValue, where); - ]]></body> - </method> - - <method name="doSearch"> - <parameter name="aData"/> - <parameter name="aWhere"/> - <body><![CDATA[ - var textBox = this._textbox; - - // Save the current value in the form history - if (aData && !PrivateBrowsingUtils.isWindowPrivate(window)) { - this.FormHistory.update( - { op : "bump", - fieldname : textBox.getAttribute("autocompletesearchparam"), - value : aData }, - { handleError : function(aError) { - Components.utils.reportError("Saving search to form history failed: " + aError.message); - }}); - } - - // null parameter below specifies HTML response for search - var submission = this.currentEngine.getSubmission(aData); - openUILinkIn(submission.uri.spec, aWhere, null, submission.postData); - ]]></body> - </method> - </implementation> - - <handlers> - <handler event="command"><![CDATA[ - const target = event.originalTarget; - if (target.engine) { - this.currentEngine = target.engine; - } else if (target.classList.contains("addengine-item")) { - var searchService = - Components.classes["@mozilla.org/browser/search-service;1"] - .getService(Components.interfaces.nsIBrowserSearchService); - // We only detect OpenSearch files - var type = Components.interfaces.nsISearchEngine.DATA_XML; - // Select the installed engine if the installation succeeds - var installCallback = { - onSuccess: engine => this.currentEngine = engine - } - searchService.addEngine(target.getAttribute("uri"), type, - target.getAttribute("src"), false, - installCallback); - } - else - return; - - this.focus(); - this.select(); - ]]></handler> - - <handler event="popupshowing" action="this.rebuildPopupDynamic();"/> - - <handler event="DOMMouseScroll" - phase="capturing" - modifiers="accel" - action="this.selectEngine(event, (event.detail > 0));"/> - - <handler event="focus"> - <![CDATA[ - // Speculatively connect to the current engine's search URI (and - // suggest URI, if different) to reduce request latency - - const SUGGEST_TYPE = "application/x-suggestions+json"; - var engine = this.currentEngine; - var connector = - Services.io.QueryInterface(Components.interfaces.nsISpeculativeConnect); - var searchURI = engine.getSubmission("dummy").uri; - let callbacks = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsIWebNavigation) - .QueryInterface(Components.interfaces.nsILoadContext); - connector.speculativeConnect(searchURI, callbacks); - - if (engine.supportsResponseType(SUGGEST_TYPE)) { - var suggestURI = engine.getSubmission("dummy", SUGGEST_TYPE).uri; - if (suggestURI.prePath != searchURI.prePath) - connector.speculativeConnect(suggestURI, callbacks); - } - ]]></handler> - </handlers> - </binding> - - <binding id="searchbar-textbox" - extends="chrome://global/content/bindings/autocomplete.xml#autocomplete"> - <implementation implements="nsIObserver"> - <constructor><![CDATA[ - const kXULNS = - "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; - - if (document.getBindingParent(this).parentNode.parentNode.localName == - "toolbarpaletteitem") - return; - - // Initialize fields - this._stringBundle = document.getBindingParent(this)._stringBundle; - this._prefBranch = - Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - this._suggestEnabled = - this._prefBranch.getBoolPref("browser.search.suggest.enabled"); - this._clickSelectsAll = - this._prefBranch.getBoolPref("browser.urlbar.clickSelectsAll"); - - this.setAttribute("clickSelectsAll", this._clickSelectsAll); - - // Add items to context menu and attach controller to handle them - var textBox = document.getAnonymousElementByAttribute(this, - "anonid", "textbox-input-box"); - var cxmenu = document.getAnonymousElementByAttribute(textBox, - "anonid", "input-box-contextmenu"); - var pasteAndSearch; - cxmenu.addEventListener("popupshowing", function() { - if (!pasteAndSearch) - return; - var controller = document.commandDispatcher.getControllerForCommand("cmd_paste"); - var enabled = controller.isCommandEnabled("cmd_paste"); - if (enabled) - pasteAndSearch.removeAttribute("disabled"); - else - pasteAndSearch.setAttribute("disabled", "true"); - }, false); - - var element, label, akey; - - element = document.createElementNS(kXULNS, "menuseparator"); - cxmenu.appendChild(element); - - var insertLocation = cxmenu.firstChild; - while (insertLocation.nextSibling && - insertLocation.getAttribute("cmd") != "cmd_paste") - insertLocation = insertLocation.nextSibling; - if (insertLocation) { - element = document.createElementNS(kXULNS, "menuitem"); - label = this._stringBundle.getString("cmd_pasteAndSearch"); - element.setAttribute("label", label); - element.setAttribute("anonid", "paste-and-search"); - element.setAttribute("oncommand", - "BrowserSearch.searchBar.select(); goDoCommand('cmd_paste'); BrowserSearch.searchBar.handleSearchCommand();"); - cxmenu.insertBefore(element, insertLocation.nextSibling); - pasteAndSearch = element; - } - - element = document.createElementNS(kXULNS, "menuitem"); - label = this._stringBundle.getString("cmd_clearHistory"); - akey = this._stringBundle.getString("cmd_clearHistory_accesskey"); - element.setAttribute("label", label); - element.setAttribute("accesskey", akey); - element.setAttribute("cmd", "cmd_clearhistory"); - cxmenu.appendChild(element); - - element = document.createElementNS(kXULNS, "menuitem"); - label = this._stringBundle.getString("cmd_showSuggestions"); - akey = this._stringBundle.getString("cmd_showSuggestions_accesskey"); - element.setAttribute("anonid", "toggle-suggest-item"); - element.setAttribute("label", label); - element.setAttribute("accesskey", akey); - element.setAttribute("cmd", "cmd_togglesuggest"); - element.setAttribute("type", "checkbox"); - element.setAttribute("checked", this._suggestEnabled); - element.setAttribute("autocheck", "false"); - this._suggestMenuItem = element; - cxmenu.appendChild(element); - - this.controllers.appendController(this.searchbarController); - - // Add observer for suggest preference - var prefs = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - prefs.addObserver("browser.search.suggest.enabled", this, false); - prefs.addObserver("browser.urlbar.clickSelectsAll", this, false); - ]]></constructor> - - <destructor><![CDATA[ - var prefs = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - prefs.removeObserver("browser.search.suggest.enabled", this); - prefs.removeObserver("browser.urlbar.clickSelectsAll", this); - - // Because XBL and the customize toolbar code interacts poorly, - // there may not be anything to remove here - try { - this.controllers.removeController(this.searchbarController); - } catch (ex) { } - ]]></destructor> - - <field name="_stringBundle"/> - <field name="_prefBranch"/> - <field name="_suggestMenuItem"/> - <field name="_suggestEnabled"/> - <field name="_clickSelectsAll"/> - - <!-- - This overrides the searchParam property in autocomplete.xml. We're - hijacking this property as a vehicle for delivering the privacy - information about the window into the guts of nsSearchSuggestions. - - Note that the setter is the same as the parent. We were not sure whether - we can override just the getter. If that proves to be the case, the setter - can be removed. - --> - <property name="searchParam" - onget="return this.getAttribute('autocompletesearchparam') + - (PrivateBrowsingUtils.isWindowPrivate(window) ? '|private' : '');" - onset="this.setAttribute('autocompletesearchparam', val); return val;"/> - - <!-- - This method overrides the autocomplete binding's openPopup (essentially - duplicating the logic from the autocomplete popup binding's - openAutocompletePopup method), modifying it so that the popup is aligned with - the inner textbox, but sized to not extend beyond the search bar border. - --> - <method name="openPopup"> - <body><![CDATA[ - var popup = this.popup; - if (!popup.mPopupOpen) { - // Initially the panel used for the searchbar (PopupAutoComplete - // in browser.xul) is hidden to avoid impacting startup / new - // window performance. The base binding's openPopup would normally - // call the overriden openAutocompletePopup in urlbarBindings.xml's - // browser-autocomplete-result-popup binding to unhide the popup, - // but since we're overriding openPopup we need to unhide the panel - // ourselves. - popup.hidden = false; - - popup.mInput = this; - popup.view = this.controller.QueryInterface(Components.interfaces.nsITreeView); - popup.invalidate(); - - popup.showCommentColumn = this.showCommentColumn; - popup.showImageColumn = this.showImageColumn; - - document.popupNode = null; - - const isRTL = getComputedStyle(this, "").direction == "rtl"; - - var outerRect = this.getBoundingClientRect(); - var innerRect = this.inputField.getBoundingClientRect(); - if (isRTL) { - var width = innerRect.right - outerRect.left; - } else { - var width = outerRect.right - innerRect.left; - } - popup.setAttribute("width", width > 100 ? width : 100); - - var yOffset = outerRect.bottom - innerRect.bottom; - popup.openPopup(this.inputField, "after_start", 0, yOffset, false, false); - } - ]]></body> - </method> - - <method name="observe"> - <parameter name="aSubject"/> - <parameter name="aTopic"/> - <parameter name="aData"/> - <body><![CDATA[ - if (aTopic == "nsPref:changed") { - switch (aData) { - case "browser.search.suggest.enabled": - this._suggestEnabled = this._prefBranch.getBoolPref(aData); - this._suggestMenuItem.setAttribute("checked", this._suggestEnabled); - break; - case "browser.urlbar.clickSelectsAll": - this._clickSelectsAll = this._prefBranch.getBoolPref(aData); - this.setAttribute("clickSelectsAll", this._clickSelectsAll); - break; - } - } - ]]></body> - </method> - - <method name="openSearch"> - <body> - <![CDATA[ - // Don't open search popup if history popup is open - if (!this.popupOpen) { - document.getBindingParent(this).searchButton.open = true; - return false; - } - return true; - ]]> - </body> - </method> - - <!-- override |onTextEntered| in autocomplete.xml --> - <method name="onTextEntered"> - <parameter name="aEvent"/> - <body><![CDATA[ - var evt = aEvent || this.mEnterEvent; - document.getBindingParent(this).handleSearchCommand(evt); - this.mEnterEvent = null; - ]]></body> - </method> - - <!-- nsIController --> - <field name="searchbarController" readonly="true"><![CDATA[({ - _self: this, - supportsCommand: function(aCommand) { - return aCommand == "cmd_clearhistory" || - aCommand == "cmd_togglesuggest"; - }, - - isCommandEnabled: function(aCommand) { - return true; - }, - - doCommand: function (aCommand) { - switch (aCommand) { - case "cmd_clearhistory": - var param = this._self.getAttribute("autocompletesearchparam"); - - let searchBar = this._self.parentNode; - - BrowserSearch.searchBar.FormHistory.update({ op : "remove", fieldname : param }, null); - this._self.value = ""; - break; - case "cmd_togglesuggest": - // The pref observer will update _suggestEnabled and the menu - // checkmark. - this._self._prefBranch.setBoolPref("browser.search.suggest.enabled", - !this._self._suggestEnabled); - break; - default: - // do nothing with unrecognized command - } - } - })]]></field> - </implementation> - - <handlers> - <handler event="keypress" keycode="VK_UP" modifiers="accel" - phase="capturing" - action="document.getBindingParent(this).selectEngine(event, false);"/> - - <handler event="keypress" keycode="VK_DOWN" modifiers="accel" - phase="capturing" - action="document.getBindingParent(this).selectEngine(event, true);"/> - - <handler event="keypress" keycode="VK_DOWN" modifiers="alt" - phase="capturing" - action="return this.openSearch();"/> - - <handler event="keypress" keycode="VK_UP" modifiers="alt" - phase="capturing" - action="return this.openSearch();"/> - -#ifndef XP_MACOSX - <handler event="keypress" keycode="VK_F4" - phase="capturing" - action="return this.openSearch();"/> -#endif - - <handler event="dragover"> - <![CDATA[ - var types = event.dataTransfer.types; - if (types.contains("text/plain") || types.contains("text/x-moz-text-internal")) - event.preventDefault(); - ]]> - </handler> - - <handler event="drop"> - <![CDATA[ - var dataTransfer = event.dataTransfer; - var data = dataTransfer.getData("text/plain"); - if (!data) - data = dataTransfer.getData("text/x-moz-text-internal"); - if (data) { - event.preventDefault(); - this.value = data; - this.onTextEntered(event); - } - ]]> - </handler> - - </handlers> - </binding> -</bindings> diff --git a/components/search/content/searchbarBindings.css b/components/search/content/searchbarBindings.css deleted file mode 100644 index b20e215..0000000 --- a/components/search/content/searchbarBindings.css +++ /dev/null @@ -1,13 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); - -.searchbar-textbox { - -moz-binding: url("chrome://browser/content/search/search.xml#searchbar-textbox"); -} - -.searchbar-engine-button { - -moz-user-focus: none; -} diff --git a/components/search/jar.mn b/components/search/jar.mn deleted file mode 100644 index e6c42f9..0000000 --- a/components/search/jar.mn +++ /dev/null @@ -1,9 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -browser.jar: -* content/browser/search/search.xml (content/search.xml) - content/browser/search/searchbarBindings.css (content/searchbarBindings.css) - content/browser/search/engineManager.xul (content/engineManager.xul) - content/browser/search/engineManager.js (content/engineManager.js) diff --git a/components/search/moz.build b/components/search/moz.build deleted file mode 100644 index c97072b..0000000 --- a/components/search/moz.build +++ /dev/null @@ -1,7 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -JAR_MANIFESTS += ['jar.mn']
\ No newline at end of file diff --git a/components/sessionstore/DocumentUtils.jsm b/components/sessionstore/DocumentUtils.jsm deleted file mode 100644 index 6b3f729..0000000 --- a/components/sessionstore/DocumentUtils.jsm +++ /dev/null @@ -1,230 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -this.EXPORTED_SYMBOLS = [ "DocumentUtils" ]; - -const Cu = Components.utils; -const Ci = Components.interfaces; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource:///modules/sessionstore/XPathGenerator.jsm"); - -this.DocumentUtils = { - /** - * Obtain form data for a DOMDocument instance. - * - * The returned object has 2 keys, "id" and "xpath". Each key holds an object - * which further defines form data. - * - * The "id" object maps element IDs to values. The "xpath" object maps the - * XPath of an element to its value. - * - * @param aDocument - * DOMDocument instance to obtain form data for. - * @return object - * Form data encoded in an object. - */ - getFormData: function DocumentUtils_getFormData(aDocument) { - let formNodes = aDocument.evaluate( - XPathGenerator.restorableFormNodes, - aDocument, - XPathGenerator.resolveNS, - Ci.nsIDOMXPathResult.UNORDERED_NODE_ITERATOR_TYPE, null - ); - - let node; - let ret = {id: {}, xpath: {}}; - - // Limit the number of XPath expressions for performance reasons. See - // bug 477564. - const MAX_TRAVERSED_XPATHS = 100; - let generatedCount = 0; - - while (node = formNodes.iterateNext()) { - let nId = node.id; - let hasDefaultValue = true; - let value; - - // Only generate a limited number of XPath expressions for perf reasons - // (cf. bug 477564) - if (!nId && generatedCount > MAX_TRAVERSED_XPATHS) { - continue; - } - - if (node instanceof Ci.nsIDOMHTMLInputElement || - node instanceof Ci.nsIDOMHTMLTextAreaElement) { - switch (node.type) { - case "checkbox": - case "radio": - value = node.checked; - hasDefaultValue = value == node.defaultChecked; - break; - case "file": - value = { type: "file", fileList: node.mozGetFileNameArray() }; - hasDefaultValue = !value.fileList.length; - break; - default: // text, textarea - value = node.value; - hasDefaultValue = value == node.defaultValue; - break; - } - } else if (!node.multiple) { - // <select>s without the multiple attribute are hard to determine the - // default value, so assume we don't have the default. - hasDefaultValue = false; - value = { selectedIndex: node.selectedIndex, value: node.value }; - } else { - // <select>s with the multiple attribute are easier to determine the - // default value since each <option> has a defaultSelected - let options = Array.map(node.options, function(aOpt, aIx) { - let oSelected = aOpt.selected; - hasDefaultValue = hasDefaultValue && (oSelected == aOpt.defaultSelected); - return oSelected ? aOpt.value : -1; - }); - value = options.filter(function(aIx) aIx !== -1); - } - - // In order to reduce XPath generation (which is slow), we only save data - // for form fields that have been changed. (cf. bug 537289) - if (!hasDefaultValue) { - if (nId) { - ret.id[nId] = value; - } else { - generatedCount++; - ret.xpath[XPathGenerator.generate(node)] = value; - } - } - } - - return ret; - }, - - /** - * Merges form data on a document from previously obtained data. - * - * This is the inverse of getFormData(). The data argument is the same object - * type which is returned by getFormData(): an object containing the keys - * "id" and "xpath" which are each objects mapping element identifiers to - * form values. - * - * Where the document has existing form data for an element, the value - * will be replaced. Where the document has a form element but no matching - * data in the passed object, the element is untouched. - * - * @param aDocument - * DOMDocument instance to which to restore form data. - * @param aData - * Object defining form data. - */ - mergeFormData: function DocumentUtils_mergeFormData(aDocument, aData) { - if ("xpath" in aData) { - for each (let [xpath, value] in Iterator(aData.xpath)) { - let node = XPathGenerator.resolve(aDocument, xpath); - - if (node) { - this.restoreFormValue(node, value, aDocument); - } - } - } - - if ("id" in aData) { - for each (let [id, value] in Iterator(aData.id)) { - let node = aDocument.getElementById(id); - - if (node) { - this.restoreFormValue(node, value, aDocument); - } - } - } - }, - - /** - * Low-level function to restore a form value to a DOMNode. - * - * If you want a higher-level interface, see mergeFormData(). - * - * When the value is changed, the function will fire the appropriate DOM - * events. - * - * @param aNode - * DOMNode to set form value on. - * @param aValue - * Value to set form element to. - * @param aDocument [optional] - * DOMDocument node belongs to. If not defined, node.ownerDocument - * is used. - */ - restoreFormValue: function DocumentUtils_restoreFormValue(aNode, aValue, aDocument) { - aDocument = aDocument || aNode.ownerDocument; - - let eventType; - - if (typeof aValue == "string" && aNode.type != "file") { - // Don't dispatch an input event if there is no change. - if (aNode.value == aValue) { - return; - } - - aNode.value = aValue; - eventType = "input"; - } else if (typeof aValue == "boolean") { - // Don't dispatch a change event for no change. - if (aNode.checked == aValue) { - return; - } - - aNode.checked = aValue; - eventType = "change"; - } else if (typeof aValue == "number") { - // handle select backwards compatibility, example { "#id" : index } - // We saved the value blindly since selects take more work to determine - // default values. So now we should check to avoid unnecessary events. - if (aNode.selectedIndex == aValue) { - return; - } - - if (aValue < aNode.options.length) { - aNode.selectedIndex = aValue; - eventType = "change"; - } - } else if (aValue && aValue.selectedIndex >= 0 && aValue.value) { - // handle select new format - - // Don't dispatch a change event for no change - if (aNode.options[aNode.selectedIndex].value == aValue.value) { - return; - } - - // find first option with matching aValue if possible - for (let i = 0; i < aNode.options.length; i++) { - if (aNode.options[i].value == aValue.value) { - aNode.selectedIndex = i; - break; - } - } - eventType = "change"; - } else if (aValue && aValue.fileList && aValue.type == "file" && - aNode.type == "file") { - aNode.mozSetFileNameArray(aValue.fileList, aValue.fileList.length); - eventType = "input"; - } else if (aValue && typeof aValue.indexOf == "function" && aNode.options) { - Array.forEach(aNode.options, function(opt, index) { - // don't worry about malformed options with same values - opt.selected = aValue.indexOf(opt.value) > -1; - - // Only fire the event here if this wasn't selected by default - if (!opt.defaultSelected) { - eventType = "change"; - } - }); - } - - // Fire events for this node if applicable - if (eventType) { - let event = aDocument.createEvent("UIEvents"); - event.initUIEvent(eventType, true, true, aDocument.defaultView, 0); - aNode.dispatchEvent(event); - } - } -}; diff --git a/components/sessionstore/SessionStorage.jsm b/components/sessionstore/SessionStorage.jsm deleted file mode 100644 index 64aef35..0000000 --- a/components/sessionstore/SessionStorage.jsm +++ /dev/null @@ -1,165 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public -* License, v. 2.0. If a copy of the MPL was not distributed with this file, -* You can obtain one at http://mozilla.org/MPL/2.0/. */ - -this.EXPORTED_SYMBOLS = ["SessionStorage"]; - -const Cu = Components.utils; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "SessionStore", - "resource:///modules/sessionstore/SessionStore.jsm"); - -this.SessionStorage = { - /** - * Updates all sessionStorage "super cookies" - * @param aDocShell - * That tab's docshell (containing the sessionStorage) - * @param aFullData - * always return privacy sensitive data (use with care) - */ - serialize: function ssto_serialize(aDocShell, aFullData) { - return DomStorage.read(aDocShell, aFullData); - }, - - /** - * Restores all sessionStorage "super cookies". - * @param aDocShell - * A tab's docshell (containing the sessionStorage) - * @param aStorageData - * Storage data to be restored - */ - deserialize: function ssto_deserialize(aDocShell, aStorageData) { - DomStorage.write(aDocShell, aStorageData); - } -}; - -Object.freeze(SessionStorage); - -var DomStorage = { - /** - * Reads all session storage data from the given docShell. - * @param aDocShell - * A tab's docshell (containing the sessionStorage) - * @param aFullData - * Always return privacy sensitive data (use with care) - */ - read: function DomStorage_read(aDocShell, aFullData) { - let data = {}; - let isPinned = aDocShell.isAppTab; - let shistory = aDocShell.sessionHistory; - - for (let i = 0; i < shistory.count; i++) { - let principal = History.getPrincipalForEntry(shistory, i, aDocShell); - if (!principal) - continue; - - // Check if we're allowed to store sessionStorage data. - let isHTTPS = principal.URI && principal.URI.schemeIs("https"); - if (aFullData || SessionStore.checkPrivacyLevel(isHTTPS, isPinned)) { - let origin = principal.extendedOrigin; - - // Don't read a host twice. - if (!(origin in data)) { - let originData = this._readEntry(principal, aDocShell); - if (Object.keys(originData).length) { - data[origin] = originData; - } - } - } - } - - return data; - }, - - /** - * Writes session storage data to the given tab. - * @param aDocShell - * A tab's docshell (containing the sessionStorage) - * @param aStorageData - * Storage data to be restored - */ - write: function DomStorage_write(aDocShell, aStorageData) { - for (let [host, data] in Iterator(aStorageData)) { - let uri = Services.io.newURI(host, null, null); - let principal = Services.scriptSecurityManager.getDocShellCodebasePrincipal(uri, aDocShell); - let storageManager = aDocShell.QueryInterface(Components.interfaces.nsIDOMStorageManager); - let window = aDocShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsIDOMWindow); - - // There is no need to pass documentURI, it's only used to fill documentURI property of - // domstorage event, which in this case has no consumer. Prevention of events in case - // of missing documentURI will be solved in a followup bug to bug 600307. - try { - let storage = storageManager.createStorage(window, principal, "", aDocShell.usePrivateBrowsing); - } catch(e) { - Cu.reportError(e); - } - - for (let [key, value] in Iterator(data)) { - try { - storage.setItem(key, value); - } catch (e) { - // throws e.g. for URIs that can't have sessionStorage - Cu.reportError(e); - } - } - } - }, - - /** - * Reads an entry in the session storage data contained in a tab's history. - * @param aURI - * That history entry uri - * @param aDocShell - * A tab's docshell (containing the sessionStorage) - */ - _readEntry: function DomStorage_readEntry(aPrincipal, aDocShell) { - let hostData = {}; - let storage; - - try { - let storageManager = aDocShell.QueryInterface(Components.interfaces.nsIDOMStorageManager); - storage = storageManager.getStorage(aPrincipal); - } catch (e) { - // sessionStorage might throw if it's turned off, see bug 458954 - } - - if (storage && storage.length) { - for (let i = 0; i < storage.length; i++) { - try { - let key = storage.key(i); - hostData[key] = storage.getItem(key); - } catch (e) { - // This currently throws for secured items (cf. bug 442048). - } - } - } - - return hostData; - } -}; - -var History = { - /** - * Returns a given history entry's URI. - * @param aHistory - * That tab's session history - * @param aIndex - * The history entry's index - * @param aDocShell - * That tab's docshell - */ - getPrincipalForEntry: function History_getPrincipalForEntry(aHistory, - aIndex, - aDocShell) { - try { - return Services.scriptSecurityManager.getDocShellCodebasePrincipal( - aHistory.getEntryAtIndex(aIndex, false).URI, aDocShell); - } catch (e) { - // This might throw for some reason. - } - }, -}; diff --git a/components/sessionstore/SessionStore.jsm b/components/sessionstore/SessionStore.jsm deleted file mode 100644 index e19a578..0000000 --- a/components/sessionstore/SessionStore.jsm +++ /dev/null @@ -1,4786 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -this.EXPORTED_SYMBOLS = ["SessionStore"]; - -const Cu = Components.utils; -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cr = Components.results; - -const STATE_STOPPED = 0; -const STATE_RUNNING = 1; -const STATE_QUITTING = -1; - -const STATE_STOPPED_STR = "stopped"; -const STATE_RUNNING_STR = "running"; - -const TAB_STATE_NEEDS_RESTORE = 1; -const TAB_STATE_RESTORING = 2; - -const PRIVACY_NONE = 0; -const PRIVACY_ENCRYPTED = 1; -const PRIVACY_FULL = 2; - -const NOTIFY_WINDOWS_RESTORED = "sessionstore-windows-restored"; -const NOTIFY_BROWSER_STATE_RESTORED = "sessionstore-browser-state-restored"; - -// Default maximum number of tabs to restore simultaneously. Controlled by -// the browser.sessionstore.max_concurrent_tabs pref. -const DEFAULT_MAX_CONCURRENT_TAB_RESTORES = 3; - -// global notifications observed -const OBSERVING = [ - "domwindowopened", "domwindowclosed", - "quit-application-requested", "quit-application-granted", - "browser-lastwindow-close-granted", - "quit-application", "browser:purge-session-history", - "browser:purge-domain-data" -]; - -// XUL Window properties to (re)store -// Restored in restoreDimensions() -const WINDOW_ATTRIBUTES = ["width", "height", "screenX", "screenY", "sizemode"]; - -// Hideable window features to (re)store -// Restored in restoreWindowFeatures() -const WINDOW_HIDEABLE_FEATURES = [ - "menubar", "toolbar", "locationbar", "personalbar", "statusbar", "scrollbars" -]; - -const MESSAGES = [ - // The content script tells us that its form data (or that of one of its - // subframes) might have changed. This can be the contents or values of - // standard form fields or of ContentEditables. - "SessionStore:input", - - // The content script has received a pageshow event. This happens when a - // page is loaded from bfcache without any network activity, i.e. when - // clicking the back or forward button. - "SessionStore:pageshow" -]; - -// These are tab events that we listen to. -const TAB_EVENTS = [ - "TabOpen", "TabClose", "TabSelect", "TabShow", "TabHide", "TabPinned", - "TabUnpinned" -]; - -#ifndef XP_WIN -#define BROKEN_WM_Z_ORDER -#endif - -Cu.import("resource://gre/modules/Services.jsm", this); -Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); -// debug.js adds NS_ASSERT. cf. bug 669196 -Cu.import("resource://gre/modules/debug.js", this); -Cu.import("resource://gre/modules/osfile.jsm", this); -Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm", this); -Cu.import("resource://gre/modules/Promise.jsm", this); - -XPCOMUtils.defineLazyServiceGetter(this, "gSessionStartup", - "@mozilla.org/browser/sessionstartup;1", "nsISessionStartup"); -XPCOMUtils.defineLazyServiceGetter(this, "gScreenManager", - "@mozilla.org/gfx/screenmanager;1", "nsIScreenManager"); - -// List of docShell capabilities to (re)store. These are automatically -// retrieved from a given docShell if not already collected before. -// This is made so they're automatically in sync with all nsIDocShell.allow* -// properties. -var gDocShellCapabilities = (function () { - let caps; - - return docShell => { - if (!caps) { - let keys = Object.keys(docShell); - caps = keys.filter(k => k.startsWith("allow")).map(k => k.slice(5)); - } - - return caps; - }; -})(); - -XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", - "resource://gre/modules/NetUtil.jsm"); - -#ifdef MOZ_DEVTOOLS -XPCOMUtils.defineLazyModuleGetter(this, "ScratchpadManager", - "resource://devtools/client/scratchpad/scratchpad-manager.jsm"); - -Object.defineProperty(this, "HUDService", { - get: function HUDService_getter() { - let devtools = Cu.import("resource://devtools/shared/Loader.jsm", {}).devtools; - return devtools.require("devtools/client/webconsole/hudservice").HUDService; - }, - configurable: true, - enumerable: true -}); -#endif - -XPCOMUtils.defineLazyModuleGetter(this, "DocumentUtils", - "resource:///modules/sessionstore/DocumentUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "SessionStorage", - "resource:///modules/sessionstore/SessionStorage.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "_SessionFile", - "resource:///modules/sessionstore/_SessionFile.jsm"); - -function debug(aMsg) { - aMsg = ("SessionStore: " + aMsg).replace(/\S{80}/g, "$&\n"); - Services.console.logStringMessage(aMsg); -} - -this.SessionStore = { - get promiseInitialized() { - return SessionStoreInternal.promiseInitialized.promise; - }, - - get canRestoreLastSession() { - return SessionStoreInternal.canRestoreLastSession; - }, - - set canRestoreLastSession(val) { - SessionStoreInternal.canRestoreLastSession = val; - }, - - init: function ss_init(aWindow) { - return SessionStoreInternal.init(aWindow); - }, - - getBrowserState: function ss_getBrowserState() { - return SessionStoreInternal.getBrowserState(); - }, - - setBrowserState: function ss_setBrowserState(aState) { - SessionStoreInternal.setBrowserState(aState); - }, - - getWindowState: function ss_getWindowState(aWindow) { - return SessionStoreInternal.getWindowState(aWindow); - }, - - setWindowState: function ss_setWindowState(aWindow, aState, aOverwrite) { - SessionStoreInternal.setWindowState(aWindow, aState, aOverwrite); - }, - - getTabState: function ss_getTabState(aTab) { - return SessionStoreInternal.getTabState(aTab); - }, - - setTabState: function ss_setTabState(aTab, aState) { - SessionStoreInternal.setTabState(aTab, aState); - }, - - duplicateTab: function ss_duplicateTab(aWindow, aTab, aDelta) { - return SessionStoreInternal.duplicateTab(aWindow, aTab, aDelta); - }, - - getClosedTabCount: function ss_getClosedTabCount(aWindow) { - return SessionStoreInternal.getClosedTabCount(aWindow); - }, - - getClosedTabData: function ss_getClosedTabDataAt(aWindow) { - return SessionStoreInternal.getClosedTabData(aWindow); - }, - - undoCloseTab: function ss_undoCloseTab(aWindow, aIndex) { - return SessionStoreInternal.undoCloseTab(aWindow, aIndex); - }, - - forgetClosedTab: function ss_forgetClosedTab(aWindow, aIndex) { - return SessionStoreInternal.forgetClosedTab(aWindow, aIndex); - }, - - getClosedWindowCount: function ss_getClosedWindowCount() { - return SessionStoreInternal.getClosedWindowCount(); - }, - - getClosedWindowData: function ss_getClosedWindowData() { - return SessionStoreInternal.getClosedWindowData(); - }, - - undoCloseWindow: function ss_undoCloseWindow(aIndex) { - return SessionStoreInternal.undoCloseWindow(aIndex); - }, - - forgetClosedWindow: function ss_forgetClosedWindow(aIndex) { - return SessionStoreInternal.forgetClosedWindow(aIndex); - }, - - getWindowValue: function ss_getWindowValue(aWindow, aKey) { - return SessionStoreInternal.getWindowValue(aWindow, aKey); - }, - - setWindowValue: function ss_setWindowValue(aWindow, aKey, aStringValue) { - SessionStoreInternal.setWindowValue(aWindow, aKey, aStringValue); - }, - - deleteWindowValue: function ss_deleteWindowValue(aWindow, aKey) { - SessionStoreInternal.deleteWindowValue(aWindow, aKey); - }, - - getTabValue: function ss_getTabValue(aTab, aKey) { - return SessionStoreInternal.getTabValue(aTab, aKey); - }, - - setTabValue: function ss_setTabValue(aTab, aKey, aStringValue) { - SessionStoreInternal.setTabValue(aTab, aKey, aStringValue); - }, - - deleteTabValue: function ss_deleteTabValue(aTab, aKey) { - SessionStoreInternal.deleteTabValue(aTab, aKey); - }, - - persistTabAttribute: function ss_persistTabAttribute(aName) { - SessionStoreInternal.persistTabAttribute(aName); - }, - - restoreLastSession: function ss_restoreLastSession() { - SessionStoreInternal.restoreLastSession(); - }, - - checkPrivacyLevel: function ss_checkPrivacyLevel(aIsHTTPS, aUseDefaultPref) { - return SessionStoreInternal.checkPrivacyLevel(aIsHTTPS, aUseDefaultPref); - } -}; - -// Freeze the SessionStore object. We don't want anyone to modify it. -Object.freeze(SessionStore); - -var SessionStoreInternal = { - QueryInterface: XPCOMUtils.generateQI([ - Ci.nsIDOMEventListener, - Ci.nsIObserver, - Ci.nsISupportsWeakReference - ]), - - // set default load state - _loadState: STATE_STOPPED, - - // During the initial restore and setBrowserState calls tracks the number of - // windows yet to be restored - _restoreCount: -1, - - // whether a setBrowserState call is in progress - _browserSetState: false, - - // time in milliseconds (Date.now()) when the session was last written to file - _lastSaveTime: 0, - - // time in milliseconds when the session was started (saved across sessions), - // defaults to now if no session was restored or timestamp doesn't exist - _sessionStartTime: Date.now(), - - // states for all currently opened windows - _windows: {}, - - // internal states for all open windows (data we need to associate, - // but not write to disk) - _internalWindows: {}, - - // states for all recently closed windows - _closedWindows: [], - - // not-"dirty" windows usually don't need to have their data updated - _dirtyWindows: {}, - - // collection of session states yet to be restored - _statesToRestore: {}, - - // counts the number of crashes since the last clean start - _recentCrashes: 0, - - // whether the last window was closed and should be restored - _restoreLastWindow: false, - - // number of tabs currently restoring - _tabsRestoringCount: 0, - - // max number of tabs to restore concurrently - _maxConcurrentTabRestores: DEFAULT_MAX_CONCURRENT_TAB_RESTORES, - - // whether restored tabs load cached versions or force a reload - _cacheBehavior: 0, - - // The state from the previous session (after restoring pinned tabs). This - // state is persisted and passed through to the next session during an app - // restart to make the third party add-on warning not trash the deferred - // session - _lastSessionState: null, - - // When starting Firefox with a single private window, this is the place - // where we keep the session we actually wanted to restore in case the user - // decides to later open a non-private window as well. - _deferredInitialState: null, - - // A promise resolved once initialization is complete - _promiseInitialization: Promise.defer(), - - // Whether session has been initialized - _sessionInitialized: false, - - // True if session store is disabled by multi-process browsing. - // See bug 516755. - _disabledForMultiProcess: false, - - // The original "sessionstore.resume_session_once" preference value before it - // was modified by saveState. saveState will set the - // "sessionstore.resume_session_once" to true when the - // the "sessionstore.resume_from_crash" preference is false (crash recovery - // is disabled) so that pinned tabs will be restored in the case of a - // crash. This variable is used to restore the original value so the - // previous session is not always restored when - // "sessionstore.resume_from_crash" is true. - _resume_session_once_on_shutdown: null, - - /** - * A promise fulfilled once initialization is complete. - */ - get promiseInitialized() { - return this._promiseInitialization; - }, - - /* ........ Public Getters .............. */ - get canRestoreLastSession() { - return this._lastSessionState; - }, - - set canRestoreLastSession(val) { - this._lastSessionState = null; - }, - - /* ........ Global Event Handlers .............. */ - - /** - * Initialize the component - */ - initService: function ssi_initService() { - if (this._sessionInitialized) { - return; - } - OBSERVING.forEach(function(aTopic) { - Services.obs.addObserver(this, aTopic, true); - }, this); - - this._initPrefs(); - - this._disabledForMultiProcess = false; - - // this pref is only read at startup, so no need to observe it - this._sessionhistory_max_entries = - this._prefBranch.getIntPref("sessionhistory.max_entries"); - - gSessionStartup.onceInitialized.then( - this.initSession.bind(this) - ); - }, - - initSession: function ssi_initSession() { - let ss = gSessionStartup; - try { - if (ss.doRestore() || - ss.sessionType == Ci.nsISessionStartup.DEFER_SESSION) - this._initialState = ss.state; - } - catch(ex) { dump(ex + "\n"); } // no state to restore, which is ok - - if (this._initialState) { - try { - // If we're doing a DEFERRED session, then we want to pull pinned tabs - // out so they can be restored. - if (ss.sessionType == Ci.nsISessionStartup.DEFER_SESSION) { - let [iniState, remainingState] = this._prepDataForDeferredRestore(this._initialState); - // If we have a iniState with windows, that means that we have windows - // with app tabs to restore. - if (iniState.windows.length) - this._initialState = iniState; - else - this._initialState = null; - if (remainingState.windows.length) - this._lastSessionState = remainingState; - } - else { - // Get the last deferred session in case the user still wants to - // restore it - this._lastSessionState = this._initialState.lastSessionState; - - let lastSessionCrashed = - this._initialState.session && this._initialState.session.state && - this._initialState.session.state == STATE_RUNNING_STR; - if (lastSessionCrashed) { - this._recentCrashes = (this._initialState.session && - this._initialState.session.recentCrashes || 0) + 1; - - if (this._needsRestorePage(this._initialState, this._recentCrashes)) { - // replace the crashed session with a restore-page-only session - let pageData = { - url: "about:sessionrestore", - formdata: { - id: { "sessionData": this._initialState }, - xpath: {} - } - }; - this._initialState = { windows: [{ tabs: [{ entries: [pageData] }] }] }; - } - } - - // Load the session start time from the previous state - this._sessionStartTime = this._initialState.session && - this._initialState.session.startTime || - this._sessionStartTime; - - // make sure that at least the first window doesn't have anything hidden - delete this._initialState.windows[0].hidden; - // Since nothing is hidden in the first window, it cannot be a popup - delete this._initialState.windows[0].isPopup; - // We don't want to minimize and then open a window at startup. - if (this._initialState.windows[0].sizemode == "minimized") - this._initialState.windows[0].sizemode = "normal"; - // clear any lastSessionWindowID attributes since those don't matter - // during normal restore - this._initialState.windows.forEach(function(aWindow) { - delete aWindow.__lastSessionWindowID; - }); - } - } - catch (ex) { debug("The session file is invalid: " + ex); } - } - - // A Lazy getter for the sessionstore.js backup promise. - XPCOMUtils.defineLazyGetter(this, "_backupSessionFileOnce", function () { - return _SessionFile.createBackupCopy(); - }); - - // at this point, we've as good as resumed the session, so we can - // clear the resume_session_once flag, if it's set - if (this._loadState != STATE_QUITTING && - this._prefBranch.getBoolPref("sessionstore.resume_session_once")) - this._prefBranch.setBoolPref("sessionstore.resume_session_once", false); - - this._initEncoding(); - - // Session is ready. - this._sessionInitialized = true; - this._promiseInitialization.resolve(); - }, - - _initEncoding : function ssi_initEncoding() { - // The (UTF-8) encoder used to write to files. - XPCOMUtils.defineLazyGetter(this, "_writeFileEncoder", function () { - return new TextEncoder(); - }); - }, - - _initPrefs : function() { - XPCOMUtils.defineLazyGetter(this, "_prefBranch", function () { - return Services.prefs.getBranch("browser."); - }); - - // minimal interval between two save operations (in milliseconds) - XPCOMUtils.defineLazyGetter(this, "_interval", function () { - // used often, so caching/observing instead of fetching on-demand - this._prefBranch.addObserver("sessionstore.interval", this, true); - return this._prefBranch.getIntPref("sessionstore.interval"); - }); - - // when crash recovery is disabled, session data is not written to disk - XPCOMUtils.defineLazyGetter(this, "_resume_from_crash", function () { - // get crash recovery state from prefs and allow for proper reaction to state changes - this._prefBranch.addObserver("sessionstore.resume_from_crash", this, true); - return this._prefBranch.getBoolPref("sessionstore.resume_from_crash"); - }); - - this._max_tabs_undo = this._prefBranch.getIntPref("sessionstore.max_tabs_undo"); - this._prefBranch.addObserver("sessionstore.max_tabs_undo", this, true); - - this._max_windows_undo = this._prefBranch.getIntPref("sessionstore.max_windows_undo"); - this._prefBranch.addObserver("sessionstore.max_windows_undo", this, true); - - // Straight-up collect the following one-time prefs - this._maxConcurrentTabRestores = - Services.prefs.getIntPref("browser.sessionstore.max_concurrent_tabs"); - // ensure a sane value for concurrency, ignore and set default otherwise - if (this._maxConcurrentTabRestores < 1 || this._maxConcurrentTabRestores > 10) { - this._maxConcurrentTabRestores = DEFAULT_MAX_CONCURRENT_TAB_RESTORES; - } - this._cacheBehavior = - Services.prefs.getIntPref("browser.sessionstore.cache_behavior"); - - }, - - _initWindow: function ssi_initWindow(aWindow) { - if (aWindow) { - this.onLoad(aWindow); - } else if (this._loadState == STATE_STOPPED) { - // If init is being called with a null window, it's possible that we - // just want to tell sessionstore that a session is live (as is the case - // with starting Firefox with -private, for example; see bug 568816), - // so we should mark the load state as running to make sure that - // things like setBrowserState calls will succeed in restoring the session. - this._loadState = STATE_RUNNING; - } - }, - - /** - * Start tracking a window. - * - * This function also initializes the component if it is not - * initialized yet. - */ - init: function ssi_init(aWindow) { - let self = this; - this.initService(); - return this._promiseInitialization.promise.then( - function onSuccess() { - self._initWindow(aWindow); - } - ); - }, - - /** - * Called on application shutdown, after notifications: - * quit-application-granted, quit-application - */ - _uninit: function ssi_uninit() { - // save all data for session resuming - if (this._sessionInitialized) - this.saveState(true); - - // clear out priority queue in case it's still holding refs - TabRestoreQueue.reset(); - - // Make sure to break our cycle with the save timer - if (this._saveTimer) { - this._saveTimer.cancel(); - this._saveTimer = null; - } - }, - - /** - * Handle notifications - */ - observe: function ssi_observe(aSubject, aTopic, aData) { - if (this._disabledForMultiProcess) - return; - - switch (aTopic) { - case "domwindowopened": // catch new windows - this.onOpen(aSubject); - break; - case "domwindowclosed": // catch closed windows - this.onClose(aSubject); - break; - case "quit-application-requested": - this.onQuitApplicationRequested(); - break; - case "quit-application-granted": - this.onQuitApplicationGranted(); - break; - case "browser-lastwindow-close-granted": - this.onLastWindowCloseGranted(); - break; - case "quit-application": - this.onQuitApplication(aData); - break; - case "browser:purge-session-history": // catch sanitization - this.onPurgeSessionHistory(); - break; - case "browser:purge-domain-data": - this.onPurgeDomainData(aData); - break; - case "nsPref:changed": // catch pref changes - this.onPrefChange(aData); - break; - case "timer-callback": // timer call back for delayed saving - this.onTimerCallback(); - break; - } - }, - - /** - * This method handles incoming messages sent by the session store content - * script and thus enables communication with OOP tabs. - */ - receiveMessage: function ssi_receiveMessage(aMessage) { - var browser = aMessage.target; - var win = browser.ownerDocument.defaultView; - - switch (aMessage.name) { - case "SessionStore:pageshow": - this.onTabLoad(win, browser); - break; - case "SessionStore:input": - this.onTabInput(win, browser); - break; - default: - debug("received unknown message '" + aMessage.name + "'"); - break; - } - - this._clearRestoringWindows(); - }, - - /* ........ Window Event Handlers .............. */ - - /** - * Implement nsIDOMEventListener for handling various window and tab events - */ - handleEvent: function ssi_handleEvent(aEvent) { - if (this._disabledForMultiProcess) - return; - - var win = aEvent.currentTarget.ownerDocument.defaultView; - switch (aEvent.type) { - case "load": - // If __SS_restore_data is set, then we need to restore the document - // (form data, scrolling, etc.). This will only happen when a tab is - // first restored. - let browser = aEvent.currentTarget; - if (browser.__SS_restore_data) - this.restoreDocument(win, browser, aEvent); - this.onTabLoad(win, browser); - break; - case "TabOpen": - this.onTabAdd(win, aEvent.originalTarget); - break; - case "TabClose": - // aEvent.detail determines if the tab was closed by moving to a different window - if (!aEvent.detail) - this.onTabClose(win, aEvent.originalTarget); - this.onTabRemove(win, aEvent.originalTarget); - break; - case "TabSelect": - this.onTabSelect(win); - break; - case "TabShow": - this.onTabShow(win, aEvent.originalTarget); - break; - case "TabHide": - this.onTabHide(win, aEvent.originalTarget); - break; - case "TabPinned": - case "TabUnpinned": - this.saveStateDelayed(win); - break; - } - - this._clearRestoringWindows(); - }, - - /** - * If it's the first window load since app start... - * - determine if we're reloading after a crash or a forced-restart - * - restore window state - * - restart downloads - * Set up event listeners for this window's tabs - * @param aWindow - * Window reference - */ - onLoad: function ssi_onLoad(aWindow) { - // return if window has already been initialized - if (aWindow && aWindow.__SSi && this._windows[aWindow.__SSi]) - return; - - // ignore non-browser windows and windows opened while shutting down - if (aWindow.document.documentElement.getAttribute("windowtype") != "navigator:browser" || - this._loadState == STATE_QUITTING) - return; - - // assign it a unique identifier (timestamp) - aWindow.__SSi = "window" + Date.now(); - - // and create its data object - this._windows[aWindow.__SSi] = { tabs: [], selected: 0, _closedTabs: [], busy: false }; - - // and create its internal data object - this._internalWindows[aWindow.__SSi] = { hosts: {} } - - let isPrivateWindow = false; - if (PrivateBrowsingUtils.isWindowPrivate(aWindow)) - this._windows[aWindow.__SSi].isPrivate = isPrivateWindow = true; - if (!this._isWindowLoaded(aWindow)) - this._windows[aWindow.__SSi]._restoring = true; - if (!aWindow.toolbar.visible) - this._windows[aWindow.__SSi].isPopup = true; - - // perform additional initialization when the first window is loading - if (this._loadState == STATE_STOPPED) { - this._loadState = STATE_RUNNING; - this._lastSaveTime = Date.now(); - - // restore a crashed session resp. resume the last session if requested - if (this._initialState) { - if (isPrivateWindow) { - // We're starting with a single private window. Save the state we - // actually wanted to restore so that we can do it later in case - // the user opens another, non-private window. - this._deferredInitialState = gSessionStartup.state; - delete this._initialState; - - // Nothing to restore now, notify observers things are complete. - Services.obs.notifyObservers(null, NOTIFY_WINDOWS_RESTORED, ""); - } else { - // make sure that the restored tabs are first in the window - this._initialState._firstTabs = true; - this._restoreCount = this._initialState.windows ? this._initialState.windows.length : 0; - this.restoreWindow(aWindow, this._initialState, - this._isCmdLineEmpty(aWindow, this._initialState)); - delete this._initialState; - - // _loadState changed from "stopped" to "running" - // force a save operation so that crashes happening during startup are correctly counted - this.saveState(true); - } - } - else { - // Nothing to restore, notify observers things are complete. - Services.obs.notifyObservers(null, NOTIFY_WINDOWS_RESTORED, ""); - - // the next delayed save request should execute immediately - this._lastSaveTime -= this._interval; - } - } - // this window was opened by _openWindowWithState - else if (!this._isWindowLoaded(aWindow)) { - let followUp = this._statesToRestore[aWindow.__SS_restoreID].windows.length == 1; - this.restoreWindow(aWindow, this._statesToRestore[aWindow.__SS_restoreID], true, followUp); - } - // The user opened another, non-private window after starting up with - // a single private one. Let's restore the session we actually wanted to - // restore at startup. - else if (this._deferredInitialState && !isPrivateWindow && - aWindow.toolbar.visible) { - - this._deferredInitialState._firstTabs = true; - this._restoreCount = this._deferredInitialState.windows ? - this._deferredInitialState.windows.length : 0; - this.restoreWindow(aWindow, this._deferredInitialState, false); - this._deferredInitialState = null; - } - else if (this._restoreLastWindow && aWindow.toolbar.visible && - this._closedWindows.length && !isPrivateWindow) { - - // default to the most-recently closed window - // don't use popup windows - let closedWindowState = null; - let closedWindowIndex; - for (let i = 0; i < this._closedWindows.length; i++) { - // Take the first non-popup, point our object at it, and break out. - if (!this._closedWindows[i].isPopup) { - closedWindowState = this._closedWindows[i]; - closedWindowIndex = i; - break; - } - } - - if (closedWindowState) { - let newWindowState; -#ifndef XP_MACOSX - if (!this._doResumeSession()) { -#endif - // We want to split the window up into pinned tabs and unpinned tabs. - // Pinned tabs should be restored. If there are any remaining tabs, - // they should be added back to _closedWindows. - // We'll cheat a little bit and reuse _prepDataForDeferredRestore - // even though it wasn't built exactly for this. - let [appTabsState, normalTabsState] = - this._prepDataForDeferredRestore({ windows: [closedWindowState] }); - - // These are our pinned tabs, which we should restore - if (appTabsState.windows.length) { - newWindowState = appTabsState.windows[0]; - delete newWindowState.__lastSessionWindowID; - } - - // In case there were no unpinned tabs, remove the window from _closedWindows - if (!normalTabsState.windows.length) { - this._closedWindows.splice(closedWindowIndex, 1); - } - // Or update _closedWindows with the modified state - else { - delete normalTabsState.windows[0].__lastSessionWindowID; - this._closedWindows[closedWindowIndex] = normalTabsState.windows[0]; - } -#ifndef XP_MACOSX - } - else { - // If we're just restoring the window, make sure it gets removed from - // _closedWindows. - this._closedWindows.splice(closedWindowIndex, 1); - newWindowState = closedWindowState; - delete newWindowState.hidden; - } -#endif - if (newWindowState) { - // Ensure that the window state isn't hidden - this._restoreCount = 1; - let state = { windows: [newWindowState] }; - this.restoreWindow(aWindow, state, this._isCmdLineEmpty(aWindow, state)); - } - } - // we actually restored the session just now. - this._prefBranch.setBoolPref("sessionstore.resume_session_once", false); - } - if (this._restoreLastWindow && aWindow.toolbar.visible) { - // always reset (if not a popup window) - // we don't want to restore a window directly after, for example, - // undoCloseWindow was executed. - this._restoreLastWindow = false; - } - - var tabbrowser = aWindow.gBrowser; - - // add tab change listeners to all already existing tabs - for (let i = 0; i < tabbrowser.tabs.length; i++) { - this.onTabAdd(aWindow, tabbrowser.tabs[i], true); - } - // notification of tab add/remove/selection/show/hide - TAB_EVENTS.forEach(function(aEvent) { - tabbrowser.tabContainer.addEventListener(aEvent, this, true); - }, this); - }, - - /** - * On window open - * @param aWindow - * Window reference - */ - onOpen: function ssi_onOpen(aWindow) { - var _this = this; - aWindow.addEventListener("load", function(aEvent) { - aEvent.currentTarget.removeEventListener("load", arguments.callee, false); - _this.onLoad(aEvent.currentTarget); - }, false); - return; - }, - - /** - * On window close... - * - remove event listeners from tabs - * - save all window data - * @param aWindow - * Window reference - */ - onClose: function ssi_onClose(aWindow) { - // this window was about to be restored - conserve its original data, if any - let isFullyLoaded = this._isWindowLoaded(aWindow); - if (!isFullyLoaded) { - if (!aWindow.__SSi) - aWindow.__SSi = "window" + Date.now(); - this._windows[aWindow.__SSi] = this._statesToRestore[aWindow.__SS_restoreID]; - delete this._statesToRestore[aWindow.__SS_restoreID]; - delete aWindow.__SS_restoreID; - } - - // ignore windows not tracked by SessionStore - if (!aWindow.__SSi || !this._windows[aWindow.__SSi]) { - return; - } - - // notify that the session store will stop tracking this window so that - // extensions can store any data about this window in session store before - // that's not possible anymore - let event = aWindow.document.createEvent("Events"); - event.initEvent("SSWindowClosing", true, false); - aWindow.dispatchEvent(event); - - if (this.windowToFocus && this.windowToFocus == aWindow) { - delete this.windowToFocus; - } - - var tabbrowser = aWindow.gBrowser; - - TAB_EVENTS.forEach(function(aEvent) { - tabbrowser.tabContainer.removeEventListener(aEvent, this, true); - }, this); - - // remove the progress listener for this window - tabbrowser.removeTabsProgressListener(gRestoreTabsProgressListener); - - let winData = this._windows[aWindow.__SSi]; - if (this._loadState == STATE_RUNNING) { // window not closed during a regular shut-down - // update all window data for a last time - this._collectWindowData(aWindow); - - if (isFullyLoaded) { - winData.title = aWindow.content.document.title || tabbrowser.selectedTab.label; - winData.title = this._replaceLoadingTitle(winData.title, tabbrowser, - tabbrowser.selectedTab); - let windows = {}; - windows[aWindow.__SSi] = winData; - this._updateCookies(windows); - } - -#ifndef XP_MACOSX - // Until we decide otherwise elsewhere, this window is part of a series - // of closing windows to quit. - winData._shouldRestore = true; -#endif - - // Save the window if it has multiple tabs or a single saveable tab and - // it's not private. - if (!winData.isPrivate && (winData.tabs.length > 1 || - (winData.tabs.length == 1 && this._shouldSaveTabState(winData.tabs[0])))) { - // we don't want to save the busy state - delete winData.busy; - - this._closedWindows.unshift(winData); - this._capClosedWindows(); - } - - // clear this window from the list - delete this._windows[aWindow.__SSi]; - delete this._internalWindows[aWindow.__SSi]; - - // save the state without this window to disk - this.saveStateDelayed(); - } - - for (let i = 0; i < tabbrowser.tabs.length; i++) { - this.onTabRemove(aWindow, tabbrowser.tabs[i], true); - } - - // Cache the window state until it is completely gone. - DyingWindowCache.set(aWindow, winData); - - delete aWindow.__SSi; - }, - - /** - * On quit application requested - */ - onQuitApplicationRequested: function ssi_onQuitApplicationRequested() { - // get a current snapshot of all windows - this._forEachBrowserWindow(function(aWindow) { - this._collectWindowData(aWindow); - }); - // we must cache this because _getMostRecentBrowserWindow will always - // return null by the time quit-application occurs - var activeWindow = this._getMostRecentBrowserWindow(); - if (activeWindow) - this.activeWindowSSiCache = activeWindow.__SSi || ""; - this._dirtyWindows = []; - }, - - /** - * On quit application granted - */ - onQuitApplicationGranted: function ssi_onQuitApplicationGranted() { - // freeze the data at what we've got (ignoring closing windows) - this._loadState = STATE_QUITTING; - }, - - /** - * On last browser window close - */ - onLastWindowCloseGranted: function ssi_onLastWindowCloseGranted() { - // last browser window is quitting. - // remember to restore the last window when another browser window is opened - // do not account for pref(resume_session_once) at this point, as it might be - // set by another observer getting this notice after us - this._restoreLastWindow = true; - }, - - /** - * On quitting application - * @param aData - * String type of quitting - */ - onQuitApplication: function ssi_onQuitApplication(aData) { - if (aData == "restart") { - this._prefBranch.setBoolPref("sessionstore.resume_session_once", true); - // The browser:purge-session-history notification fires after the - // quit-application notification so unregister the - // browser:purge-session-history notification to prevent clearing - // session data on disk on a restart. It is also unnecessary to - // perform any other sanitization processing on a restart as the - // browser is about to exit anyway. - Services.obs.removeObserver(this, "browser:purge-session-history"); - } - else if (this._resume_session_once_on_shutdown != null) { - // if the sessionstore.resume_session_once preference was changed by - // saveState because crash recovery is disabled then restore the - // preference back to the value it was prior to that. This will prevent - // SessionStore from always restoring the session when crash recovery is - // disabled. - this._prefBranch.setBoolPref("sessionstore.resume_session_once", - this._resume_session_once_on_shutdown); - } - - if (aData != "restart") { - // Throw away the previous session on shutdown - this._lastSessionState = null; - } - - this._loadState = STATE_QUITTING; // just to be sure - this._uninit(); - }, - - /** - * On purge of session history - */ - onPurgeSessionHistory: function ssi_onPurgeSessionHistory() { - var _this = this; - _SessionFile.wipe(); - // If the browser is shutting down, simply return after clearing the - // session data on disk as this notification fires after the - // quit-application notification so the browser is about to exit. - if (this._loadState == STATE_QUITTING) - return; - this._lastSessionState = null; - let openWindows = {}; - this._forEachBrowserWindow(function(aWindow) { - Array.forEach(aWindow.gBrowser.tabs, function(aTab) { - delete aTab.linkedBrowser.__SS_data; - delete aTab.linkedBrowser.__SS_tabStillLoading; - delete aTab.linkedBrowser.__SS_formDataSaved; - delete aTab.linkedBrowser.__SS_hostSchemeData; - if (aTab.linkedBrowser.__SS_restoreState) - this._resetTabRestoringState(aTab); - }, this); - openWindows[aWindow.__SSi] = true; - }); - // also clear all data about closed tabs and windows - for (let ix in this._windows) { - if (ix in openWindows) { - this._windows[ix]._closedTabs = []; - } - else { - delete this._windows[ix]; - delete this._internalWindows[ix]; - } - } - // also clear all data about closed windows - this._closedWindows = []; - // give the tabbrowsers a chance to clear their histories first - var win = this._getMostRecentBrowserWindow(); - if (win) - win.setTimeout(function() { _this.saveState(true); }, 0); - else if (this._loadState == STATE_RUNNING) - this.saveState(true); - // Delete the private browsing backed up state, if any - if ("_stateBackup" in this) - delete this._stateBackup; - - this._clearRestoringWindows(); - }, - - /** - * On purge of domain data - * @param aData - * String domain data - */ - onPurgeDomainData: function ssi_onPurgeDomainData(aData) { - // does a session history entry contain a url for the given domain? - function containsDomain(aEntry) { - try { - if (this._getURIFromString(aEntry.url).host.hasRootDomain(aData)) - return true; - } - catch (ex) { /* url had no host at all */ } - return aEntry.children && aEntry.children.some(containsDomain, this); - } - // remove all closed tabs containing a reference to the given domain - for (let ix in this._windows) { - let closedTabs = this._windows[ix]._closedTabs; - for (let i = closedTabs.length - 1; i >= 0; i--) { - if (closedTabs[i].state.entries.some(containsDomain, this)) - closedTabs.splice(i, 1); - } - } - // remove all open & closed tabs containing a reference to the given - // domain in closed windows - for (let ix = this._closedWindows.length - 1; ix >= 0; ix--) { - let closedTabs = this._closedWindows[ix]._closedTabs; - let openTabs = this._closedWindows[ix].tabs; - let openTabCount = openTabs.length; - for (let i = closedTabs.length - 1; i >= 0; i--) - if (closedTabs[i].state.entries.some(containsDomain, this)) - closedTabs.splice(i, 1); - for (let j = openTabs.length - 1; j >= 0; j--) { - if (openTabs[j].entries.some(containsDomain, this)) { - openTabs.splice(j, 1); - if (this._closedWindows[ix].selected > j) - this._closedWindows[ix].selected--; - } - } - if (openTabs.length == 0) { - this._closedWindows.splice(ix, 1); - } - else if (openTabs.length != openTabCount) { - // Adjust the window's title if we removed an open tab - let selectedTab = openTabs[this._closedWindows[ix].selected - 1]; - // some duplication from restoreHistory - make sure we get the correct title - let activeIndex = (selectedTab.index || selectedTab.entries.length) - 1; - if (activeIndex >= selectedTab.entries.length) - activeIndex = selectedTab.entries.length - 1; - this._closedWindows[ix].title = selectedTab.entries[activeIndex].title; - } - } - if (this._loadState == STATE_RUNNING) - this.saveState(true); - - this._clearRestoringWindows(); - }, - - /** - * On preference change - * @param aData - * String preference changed - */ - onPrefChange: function ssi_onPrefChange(aData) { - switch (aData) { - // if the user decreases the max number of closed tabs they want - // preserved update our internal states to match that max - case "sessionstore.max_tabs_undo": - this._max_tabs_undo = this._prefBranch.getIntPref("sessionstore.max_tabs_undo"); - for (let ix in this._windows) { - this._windows[ix]._closedTabs.splice(this._max_tabs_undo, this._windows[ix]._closedTabs.length); - } - break; - case "sessionstore.max_windows_undo": - this._max_windows_undo = this._prefBranch.getIntPref("sessionstore.max_windows_undo"); - this._capClosedWindows(); - break; - case "sessionstore.interval": - this._interval = this._prefBranch.getIntPref("sessionstore.interval"); - // reset timer and save - if (this._saveTimer) { - this._saveTimer.cancel(); - this._saveTimer = null; - } - this.saveStateDelayed(null, -1); - break; - case "sessionstore.resume_from_crash": - this._resume_from_crash = this._prefBranch.getBoolPref("sessionstore.resume_from_crash"); - // restore original resume_session_once preference if set in saveState - if (this._resume_session_once_on_shutdown != null) { - this._prefBranch.setBoolPref("sessionstore.resume_session_once", - this._resume_session_once_on_shutdown); - this._resume_session_once_on_shutdown = null; - } - // either create the file with crash recovery information or remove it - // (when _loadState is not STATE_RUNNING, that file is used for session resuming instead) - if (!this._resume_from_crash) - _SessionFile.wipe(); - this.saveState(true); - break; - } - }, - - /** - * On timer callback - */ - onTimerCallback: function ssi_onTimerCallback() { - this._saveTimer = null; - this.saveState(); - }, - - /** - * set up listeners for a new tab - * @param aWindow - * Window reference - * @param aTab - * Tab reference - * @param aNoNotification - * bool Do not save state if we're updating an existing tab - */ - onTabAdd: function ssi_onTabAdd(aWindow, aTab, aNoNotification) { - let browser = aTab.linkedBrowser; - browser.addEventListener("load", this, true); - - let mm = browser.messageManager; - MESSAGES.forEach(msg => mm.addMessageListener(msg, this)); - - if (!aNoNotification) { - this.saveStateDelayed(aWindow); - } - }, - - /** - * remove listeners for a tab - * @param aWindow - * Window reference - * @param aTab - * Tab reference - * @param aNoNotification - * bool Do not save state if we're updating an existing tab - */ - onTabRemove: function ssi_onTabRemove(aWindow, aTab, aNoNotification) { - let browser = aTab.linkedBrowser; - browser.removeEventListener("load", this, true); - - let mm = browser.messageManager; - MESSAGES.forEach(msg => mm.removeMessageListener(msg, this)); - - delete browser.__SS_data; - delete browser.__SS_tabStillLoading; - delete browser.__SS_formDataSaved; - delete browser.__SS_hostSchemeData; - - // If this tab was in the middle of restoring or still needs to be restored, - // we need to reset that state. If the tab was restoring, we will attempt to - // restore the next tab. - let previousState = browser.__SS_restoreState; - if (previousState) { - this._resetTabRestoringState(aTab); - if (previousState == TAB_STATE_RESTORING) - this.restoreNextTab(); - } - - if (!aNoNotification) { - this.saveStateDelayed(aWindow); - } - }, - - /** - * When a tab closes, collect its properties - * @param aWindow - * Window reference - * @param aTab - * Tab reference - */ - onTabClose: function ssi_onTabClose(aWindow, aTab) { - // notify the tabbrowser that the tab state will be retrieved for the last time - // (so that extension authors can easily set data on soon-to-be-closed tabs) - var event = aWindow.document.createEvent("Events"); - event.initEvent("SSTabClosing", true, false); - aTab.dispatchEvent(event); - - // don't update our internal state if we don't have to - if (this._max_tabs_undo == 0) { - return; - } - - // make sure that the tab related data is up-to-date - var tabState = this._collectTabData(aTab); - this._updateTextAndScrollDataForTab(aWindow, aTab.linkedBrowser, tabState); - - // store closed-tab data for undo - if (this._shouldSaveTabState(tabState)) { - let tabTitle = aTab.label; - let tabbrowser = aWindow.gBrowser; - tabTitle = this._replaceLoadingTitle(tabTitle, tabbrowser, aTab); - - this._windows[aWindow.__SSi]._closedTabs.unshift({ - state: tabState, - title: tabTitle, - image: tabbrowser.getIcon(aTab), - pos: aTab._tPos - }); - var length = this._windows[aWindow.__SSi]._closedTabs.length; - if (length > this._max_tabs_undo) - this._windows[aWindow.__SSi]._closedTabs.splice(this._max_tabs_undo, length - this._max_tabs_undo); - } - }, - - /** - * When a tab loads, save state. - * @param aWindow - * Window reference - * @param aBrowser - * Browser reference - */ - onTabLoad: function ssi_onTabLoad(aWindow, aBrowser) { - // react on "load" and solitary "pageshow" events (the first "pageshow" - // following "load" is too late for deleting the data caches) - // It's possible to get a load event after calling stop on a browser (when - // overwriting tabs). We want to return early if the tab hasn't been restored yet. - if (aBrowser.__SS_restoreState && - aBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) { - return; - } - - delete aBrowser.__SS_data; - delete aBrowser.__SS_tabStillLoading; - delete aBrowser.__SS_formDataSaved; - this.saveStateDelayed(aWindow); - - }, - - /** - * Called when a browser sends the "input" notification - * @param aWindow - * Window reference - * @param aBrowser - * Browser reference - */ - onTabInput: function ssi_onTabInput(aWindow, aBrowser) { - // deleting __SS_formDataSaved will cause us to recollect form data - delete aBrowser.__SS_formDataSaved; - - this.saveStateDelayed(aWindow, 3000); - }, - - /** - * When a tab is selected, save session data - * @param aWindow - * Window reference - */ - onTabSelect: function ssi_onTabSelect(aWindow) { - if (this._loadState == STATE_RUNNING) { - this._windows[aWindow.__SSi].selected = aWindow.gBrowser.tabContainer.selectedIndex; - - let tab = aWindow.gBrowser.selectedTab; - // If __SS_restoreState is still on the browser and it is - // TAB_STATE_NEEDS_RESTORE, then then we haven't restored - // this tab yet. Explicitly call restoreTab to kick off the restore. - if (tab.linkedBrowser.__SS_restoreState && - tab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) - this.restoreTab(tab); - - } - }, - - onTabShow: function ssi_onTabShow(aWindow, aTab) { - // If the tab hasn't been restored yet, move it into the right bucket - if (aTab.linkedBrowser.__SS_restoreState && - aTab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) { - TabRestoreQueue.hiddenToVisible(aTab); - - // let's kick off tab restoration again to ensure this tab gets restored - // with "restore_hidden_tabs" == false (now that it has become visible) - this.restoreNextTab(); - } - - // Default delay of 2 seconds gives enough time to catch multiple TabShow - // events due to changing groups in Panorama. - this.saveStateDelayed(aWindow); - }, - - onTabHide: function ssi_onTabHide(aWindow, aTab) { - // If the tab hasn't been restored yet, move it into the right bucket - if (aTab.linkedBrowser.__SS_restoreState && - aTab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) { - TabRestoreQueue.visibleToHidden(aTab); - } - - // Default delay of 2 seconds gives enough time to catch multiple TabHide - // events due to changing groups in Panorama. - this.saveStateDelayed(aWindow); - }, - - /* ........ nsISessionStore API .............. */ - - getBrowserState: function ssi_getBrowserState() { - return this._toJSONString(this._getCurrentState()); - }, - - setBrowserState: function ssi_setBrowserState(aState) { - this._handleClosedWindows(); - - try { - var state = JSON.parse(aState); - } - catch (ex) { /* invalid state object - don't restore anything */ } - if (!state || !state.windows) - throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); - - this._browserSetState = true; - - // Make sure the priority queue is emptied out - this._resetRestoringState(); - - var window = this._getMostRecentBrowserWindow(); - if (!window) { - this._restoreCount = 1; - this._openWindowWithState(state); - return; - } - - // close all other browser windows - this._forEachBrowserWindow(function(aWindow) { - if (aWindow != window) { - aWindow.close(); - this.onClose(aWindow); - } - }); - - // make sure closed window data isn't kept - this._closedWindows = []; - - // determine how many windows are meant to be restored - this._restoreCount = state.windows ? state.windows.length : 0; - - // restore to the given state - this.restoreWindow(window, state, true); - }, - - getWindowState: function ssi_getWindowState(aWindow) { - if ("__SSi" in aWindow) { - return this._toJSONString(this._getWindowState(aWindow)); - } - - if (DyingWindowCache.has(aWindow)) { - let data = DyingWindowCache.get(aWindow); - return this._toJSONString({ windows: [data] }); - } - - throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); - }, - - setWindowState: function ssi_setWindowState(aWindow, aState, aOverwrite) { - if (!aWindow.__SSi) - throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); - - this.restoreWindow(aWindow, aState, aOverwrite); - }, - - getTabState: function ssi_getTabState(aTab) { - if (!aTab.ownerDocument || !aTab.ownerDocument.defaultView.__SSi) - throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); - - var tabState = this._collectTabData(aTab); - - var window = aTab.ownerDocument.defaultView; - this._updateTextAndScrollDataForTab(window, aTab.linkedBrowser, tabState); - - return this._toJSONString(tabState); - }, - - setTabState: function ssi_setTabState(aTab, aState) { - var tabState = JSON.parse(aState); - if (!tabState.entries || !aTab.ownerDocument || !aTab.ownerDocument.defaultView.__SSi) - throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); - - var window = aTab.ownerDocument.defaultView; - this._setWindowStateBusy(window); - this.restoreHistoryPrecursor(window, [aTab], [tabState], 0, 0, 0); - }, - - duplicateTab: function ssi_duplicateTab(aWindow, aTab, aDelta) { - if (!aTab.ownerDocument || !aTab.ownerDocument.defaultView.__SSi || - !aWindow.getBrowser) - throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); - - var tabState = this._collectTabData(aTab, true); - var sourceWindow = aTab.ownerDocument.defaultView; - this._updateTextAndScrollDataForTab(sourceWindow, aTab.linkedBrowser, tabState, true); - tabState.index += aDelta; - tabState.index = Math.max(1, Math.min(tabState.index, tabState.entries.length)); - tabState.pinned = false; - - this._setWindowStateBusy(aWindow); - let newTab = aTab == aWindow.gBrowser.selectedTab ? - aWindow.gBrowser.addTab(null, {relatedToCurrent: true, ownerTab: aTab}) : - aWindow.gBrowser.addTab(); - - this.restoreHistoryPrecursor(aWindow, [newTab], [tabState], 0, 0, 0, - true /* Load this tab right away. */); - - return newTab; - }, - - getClosedTabCount: function ssi_getClosedTabCount(aWindow) { - if ("__SSi" in aWindow) { - return this._windows[aWindow.__SSi]._closedTabs.length; - } - - if (DyingWindowCache.has(aWindow)) { - return DyingWindowCache.get(aWindow)._closedTabs.length; - } - - throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); - }, - - getClosedTabData: function ssi_getClosedTabDataAt(aWindow) { - if ("__SSi" in aWindow) { - return this._toJSONString(this._windows[aWindow.__SSi]._closedTabs); - } - - if (DyingWindowCache.has(aWindow)) { - let data = DyingWindowCache.get(aWindow); - return this._toJSONString(data._closedTabs); - } - - throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); - }, - - undoCloseTab: function ssi_undoCloseTab(aWindow, aIndex) { - if (!aWindow.__SSi) - throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); - - var closedTabs = this._windows[aWindow.__SSi]._closedTabs; - - // default to the most-recently closed tab - aIndex = aIndex || 0; - if (!(aIndex in closedTabs)) - throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); - - // fetch the data of closed tab, while removing it from the array - let closedTab = closedTabs.splice(aIndex, 1).shift(); - let closedTabState = closedTab.state; - - this._setWindowStateBusy(aWindow); - // create a new tab - let tabbrowser = aWindow.gBrowser; - let tab = tabbrowser.addTab(); - - // restore tab content - this.restoreHistoryPrecursor(aWindow, [tab], [closedTabState], 1, 0, 0); - - // restore the tab's position - tabbrowser.moveTabTo(tab, closedTab.pos); - - // focus the tab's content area (bug 342432) - tab.linkedBrowser.focus(); - - return tab; - }, - - forgetClosedTab: function ssi_forgetClosedTab(aWindow, aIndex) { - if (!aWindow.__SSi) - throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); - - var closedTabs = this._windows[aWindow.__SSi]._closedTabs; - - // default to the most-recently closed tab - aIndex = aIndex || 0; - if (!(aIndex in closedTabs)) - throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); - - // remove closed tab from the array - closedTabs.splice(aIndex, 1); - }, - - getClosedWindowCount: function ssi_getClosedWindowCount() { - return this._closedWindows.length; - }, - - getClosedWindowData: function ssi_getClosedWindowData() { - return this._toJSONString(this._closedWindows); - }, - - undoCloseWindow: function ssi_undoCloseWindow(aIndex) { - if (!(aIndex in this._closedWindows)) - throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); - - // reopen the window - let state = { windows: this._closedWindows.splice(aIndex, 1) }; - let window = this._openWindowWithState(state); - this.windowToFocus = window; - return window; - }, - - forgetClosedWindow: function ssi_forgetClosedWindow(aIndex) { - // default to the most-recently closed window - aIndex = aIndex || 0; - if (!(aIndex in this._closedWindows)) - throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); - - // remove closed window from the array - this._closedWindows.splice(aIndex, 1); - }, - - getWindowValue: function ssi_getWindowValue(aWindow, aKey) { - if ("__SSi" in aWindow) { - var data = this._windows[aWindow.__SSi].extData || {}; - return data[aKey] || ""; - } - - if (DyingWindowCache.has(aWindow)) { - let data = DyingWindowCache.get(aWindow).extData || {}; - return data[aKey] || ""; - } - - throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); - }, - - setWindowValue: function ssi_setWindowValue(aWindow, aKey, aStringValue) { - if (aWindow.__SSi) { - if (!this._windows[aWindow.__SSi].extData) { - this._windows[aWindow.__SSi].extData = {}; - } - this._windows[aWindow.__SSi].extData[aKey] = aStringValue; - this.saveStateDelayed(aWindow); - } - else { - throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); - } - }, - - deleteWindowValue: function ssi_deleteWindowValue(aWindow, aKey) { - if (aWindow.__SSi && this._windows[aWindow.__SSi].extData && - this._windows[aWindow.__SSi].extData[aKey]) - delete this._windows[aWindow.__SSi].extData[aKey]; - }, - - getTabValue: function ssi_getTabValue(aTab, aKey) { - let data = {}; - if (aTab.__SS_extdata) { - data = aTab.__SS_extdata; - } - else if (aTab.linkedBrowser.__SS_data && aTab.linkedBrowser.__SS_data.extData) { - // If the tab hasn't been fully restored, get the data from the to-be-restored data - data = aTab.linkedBrowser.__SS_data.extData; - } - return data[aKey] || ""; - }, - - setTabValue: function ssi_setTabValue(aTab, aKey, aStringValue) { - // If the tab hasn't been restored, then set the data there, otherwise we - // could lose newly added data. - let saveTo; - if (aTab.__SS_extdata) { - saveTo = aTab.__SS_extdata; - } - else if (aTab.linkedBrowser.__SS_data && aTab.linkedBrowser.__SS_data.extData) { - saveTo = aTab.linkedBrowser.__SS_data.extData; - } - else { - aTab.__SS_extdata = {}; - saveTo = aTab.__SS_extdata; - } - saveTo[aKey] = aStringValue; - this.saveStateDelayed(aTab.ownerDocument.defaultView); - }, - - deleteTabValue: function ssi_deleteTabValue(aTab, aKey) { - // We want to make sure that if data is accessed early, we attempt to delete - // that data from __SS_data as well. Otherwise we'll throw in cases where - // data can be set or read. - let deleteFrom; - if (aTab.__SS_extdata) { - deleteFrom = aTab.__SS_extdata; - } - else if (aTab.linkedBrowser.__SS_data && aTab.linkedBrowser.__SS_data.extData) { - deleteFrom = aTab.linkedBrowser.__SS_data.extData; - } - - if (deleteFrom && deleteFrom[aKey]) - delete deleteFrom[aKey]; - }, - - persistTabAttribute: function ssi_persistTabAttribute(aName) { - if (TabAttributes.persist(aName)) { - this.saveStateDelayed(); - } - }, - - /** - * Restores the session state stored in _lastSessionState. This will attempt - * to merge data into the current session. If a window was opened at startup - * with pinned tab(s), then the remaining data from the previous session for - * that window will be opened into that winddow. Otherwise new windows will - * be opened. - */ - restoreLastSession: function ssi_restoreLastSession() { - // Use the public getter since it also checks PB mode - if (!this.canRestoreLastSession) - throw (Components.returnCode = Cr.NS_ERROR_FAILURE); - - // First collect each window with its id... - let windows = {}; - this._forEachBrowserWindow(function(aWindow) { - if (aWindow.__SS_lastSessionWindowID) - windows[aWindow.__SS_lastSessionWindowID] = aWindow; - }); - - let lastSessionState = this._lastSessionState; - - // This shouldn't ever be the case... - if (!lastSessionState.windows.length) - throw (Components.returnCode = Cr.NS_ERROR_UNEXPECTED); - - // We're technically doing a restore, so set things up so we send the - // notification when we're done. We want to send "sessionstore-browser-state-restored". - this._restoreCount = lastSessionState.windows.length; - this._browserSetState = true; - - // We want to re-use the last opened window instead of opening a new one in - // the case where it's "empty" and not associated with a window in the session. - // We will do more processing via _prepWindowToRestoreInto if we need to use - // the lastWindow. - let lastWindow = this._getMostRecentBrowserWindow(); - let canUseLastWindow = lastWindow && - !lastWindow.__SS_lastSessionWindowID; - - // Restore into windows or open new ones as needed. - for (let i = 0; i < lastSessionState.windows.length; i++) { - let winState = lastSessionState.windows[i]; - let lastSessionWindowID = winState.__lastSessionWindowID; - // delete lastSessionWindowID so we don't add that to the window again - delete winState.__lastSessionWindowID; - - // See if we can use an open window. First try one that is associated with - // the state we're trying to restore and then fallback to the last selected - // window. - let windowToUse = windows[lastSessionWindowID]; - if (!windowToUse && canUseLastWindow) { - windowToUse = lastWindow; - canUseLastWindow = false; - } - - let [canUseWindow, canOverwriteTabs] = this._prepWindowToRestoreInto(windowToUse); - - // If there's a window already open that we can restore into, use that - if (canUseWindow) { - // Since we're not overwriting existing tabs, we want to merge _closedTabs, - // putting existing ones first. Then make sure we're respecting the max pref. - if (winState._closedTabs && winState._closedTabs.length) { - let curWinState = this._windows[windowToUse.__SSi]; - curWinState._closedTabs = curWinState._closedTabs.concat(winState._closedTabs); - curWinState._closedTabs.splice(this._prefBranch.getIntPref("sessionstore.max_tabs_undo"), curWinState._closedTabs.length); - } - - // Restore into that window - pretend it's a followup since we'll already - // have a focused window. - //XXXzpao This is going to merge extData together (taking what was in - // winState over what is in the window already. The hack we have - // in _preWindowToRestoreInto will prevent most (all?) Panorama - // weirdness but we will still merge other extData. - // Bug 588217 should make this go away by merging the group data. - this.restoreWindow(windowToUse, { windows: [winState] }, canOverwriteTabs, true); - } - else { - this._openWindowWithState({ windows: [winState] }); - } - } - - // Merge closed windows from this session with ones from last session - if (lastSessionState._closedWindows) { - this._closedWindows = this._closedWindows.concat(lastSessionState._closedWindows); - this._capClosedWindows(); - } - -#ifdef MOZ_DEVTOOLS - // Scratchpad - if (lastSessionState.scratchpads) { - ScratchpadManager.restoreSession(lastSessionState.scratchpads); - } - - // The Browser Console - if (lastSessionState.browserConsole) { - HUDService.restoreBrowserConsoleSession(); - } -#endif - - // Set data that persists between sessions - this._recentCrashes = lastSessionState.session && - lastSessionState.session.recentCrashes || 0; - this._sessionStartTime = lastSessionState.session && - lastSessionState.session.startTime || - this._sessionStartTime; - - this._lastSessionState = null; - }, - - /** - * See if aWindow is usable for use when restoring a previous session via - * restoreLastSession. If usable, prepare it for use. - * - * @param aWindow - * the window to inspect & prepare - * @returns [canUseWindow, canOverwriteTabs] - * canUseWindow: can the window be used to restore into - * canOverwriteTabs: all of the current tabs are home pages and we - * can overwrite them - */ - _prepWindowToRestoreInto: function ssi_prepWindowToRestoreInto(aWindow) { - if (!aWindow) - return [false, false]; - - let event = aWindow.document.createEvent("Events"); - event.initEvent("SSRestoreIntoWindow", true, true); - - // Check if we can use the window. - if (!aWindow.dispatchEvent(event)) - return [false, false]; - - // We might be able to overwrite the existing tabs instead of just adding - // the previous session's tabs to the end. This will be set if possible. - let canOverwriteTabs = false; - - // Look at the open tabs in comparison to home pages. If all the tabs are - // home pages then we'll end up overwriting all of them. Otherwise we'll - // just close the tabs that match home pages. Tabs with the about:blank - // URI will always be overwritten. - let homePages = ["about:blank"]; - let removableTabs = []; - let tabbrowser = aWindow.gBrowser; - let normalTabsLen = tabbrowser.tabs.length - tabbrowser._numPinnedTabs; - let startupPref = this._prefBranch.getIntPref("startup.page"); - if (startupPref == 1) - homePages = homePages.concat(aWindow.gHomeButton.getHomePage().split("|")); - - for (let i = tabbrowser._numPinnedTabs; i < tabbrowser.tabs.length; i++) { - let tab = tabbrowser.tabs[i]; - if (homePages.indexOf(tab.linkedBrowser.currentURI.spec) != -1) { - removableTabs.push(tab); - } - } - - if (tabbrowser.tabs.length == removableTabs.length) { - canOverwriteTabs = true; - } - else { - // If we're not overwriting all of the tabs, then close the home tabs. - for (let i = removableTabs.length - 1; i >= 0; i--) { - tabbrowser.removeTab(removableTabs.pop(), { animate: false }); - } - } - - return [true, canOverwriteTabs]; - }, - - /* ........ Saving Functionality .............. */ - - /** - * Store all session data for a window - * @param aWindow - * Window reference - */ - _saveWindowHistory: function ssi_saveWindowHistory(aWindow) { - var tabbrowser = aWindow.gBrowser; - var tabs = tabbrowser.tabs; - var tabsData = this._windows[aWindow.__SSi].tabs = []; - - for (var i = 0; i < tabs.length; i++) - tabsData.push(this._collectTabData(tabs[i])); - - this._windows[aWindow.__SSi].selected = tabbrowser.mTabBox.selectedIndex + 1; - }, - - /** - * Collect data related to a single tab - * @param aTab - * tabbrowser tab - * @param aFullData - * always return privacy sensitive data (use with care) - * @returns object - */ - _collectTabData: function ssi_collectTabData(aTab, aFullData) { - var tabData = { entries: [], lastAccessed: aTab.lastAccessed }; - var browser = aTab.linkedBrowser; - - if (!browser || !browser.currentURI) - // can happen when calling this function right after .addTab() - return tabData; - else if (browser.__SS_data && browser.__SS_tabStillLoading) { - // use the data to be restored when the tab hasn't been completely loaded - tabData = browser.__SS_data; - if (aTab.pinned) - tabData.pinned = true; - else - delete tabData.pinned; - tabData.hidden = aTab.hidden; - - // If __SS_extdata is set then we'll use that since it might be newer. - if (aTab.__SS_extdata) - tabData.extData = aTab.__SS_extdata; - // If it exists but is empty then a key was likely deleted. In that case just - // delete extData. - if (tabData.extData && !Object.keys(tabData.extData).length) - delete tabData.extData; - return tabData; - } - - var history = null; - try { - history = browser.sessionHistory; - } - catch (ex) { } // this could happen if we catch a tab during (de)initialization - - // Limit number of back/forward button history entries to save - let oldest, newest; - let maxSerializeBack = this._prefBranch.getIntPref("sessionstore.max_serialize_back"); - if (maxSerializeBack >= 0) { - oldest = Math.max(0, history.index - maxSerializeBack); - } else { // History.getEntryAtIndex(0, ...) is the oldest. - oldest = 0; - } - let maxSerializeFwd = this._prefBranch.getIntPref("sessionstore.max_serialize_forward"); - if (maxSerializeFwd >= 0) { - newest = Math.min(history.count - 1, history.index + maxSerializeFwd); - } else { // History.getEntryAtIndex(history.count - 1, ...) is the newest. - newest = history.count - 1; - } - - // XXXzeniko anchor navigation doesn't reset __SS_data, so we could reuse - // data even when we shouldn't (e.g. Back, different anchor) - // Warning: this is required to save form data and scrolling position! - if (history && browser.__SS_data && - browser.__SS_data.entries[history.index] && - browser.__SS_data.entries[history.index].url == browser.currentURI.spec && - history.index < this._sessionhistory_max_entries - 1 && !aFullData) { - try { - tabData.entries = browser.__SS_data.entries.slice(oldest, newest + 1); - } - catch (ex) { - // No errors are expected above, but we use try-catch to keep sessionstore.js safe - NS_ASSERT(false, "SessionStore failed to slice history from browser.__SS_data"); - } - - // Set the one-based index of the currently active tab, ensuring it isn't out of bounds - tabData.index = Math.min(history.index - oldest + 1, tabData.entries.length); - } - else if (history && history.count > 0) { - browser.__SS_hostSchemeData = []; - try { - for (var j = oldest; j <= newest; j++) { - let entry = this._serializeHistoryEntry(history.getEntryAtIndex(j, false), - aFullData, aTab.pinned, browser.__SS_hostSchemeData); - tabData.entries.push(entry); - } - } - catch (ex) { - // In some cases, getEntryAtIndex will throw. This seems to be due to - // history.count being higher than it should be. By doing this in a - // try-catch, we'll update history to where it breaks, assert for - // non-release builds, and still save sessionstore.js. - NS_ASSERT(false, "SessionStore failed gathering complete history " + - "for the focused window/tab. See bug 669196."); - } - - // Set the one-based index of the currently active tab, ensuring it isn't out of bounds - tabData.index = Math.min(history.index - oldest + 1, tabData.entries.length); - - // make sure not to cache privacy sensitive data which shouldn't get out - if (!aFullData) - browser.__SS_data = tabData; - } - else if (browser.currentURI.spec != "about:blank" || - browser.contentDocument.body.hasChildNodes()) { - tabData.entries[0] = { url: browser.currentURI.spec }; - tabData.index = 1; - } - - // If there is a userTypedValue set, then either the user has typed something - // in the URL bar, or a new tab was opened with a URI to load. userTypedClear - // is used to indicate whether the tab was in some sort of loading state with - // userTypedValue. - if (browser.userTypedValue) { - tabData.userTypedValue = browser.userTypedValue; - // We always used to keep track of the loading state as an integer, where - // '0' indicated the user had typed since the last load (or no load was - // ongoing), and any positive value indicated we had started a load since - // the last time the user typed in the URL bar. Mimic this to keep the - // session store representation in sync, even though we now represent this - // more explicitly: - tabData.userTypedClear = browser.didStartLoadSinceLastUserTyping() ? 1 : 0; - } else { - delete tabData.userTypedValue; - delete tabData.userTypedClear; - } - - if (aTab.pinned) - tabData.pinned = true; - else - delete tabData.pinned; - tabData.hidden = aTab.hidden; - - var disallow = []; - for (let cap of gDocShellCapabilities(browser.docShell)) - if (!browser.docShell["allow" + cap]) - disallow.push(cap); - if (disallow.length > 0) - tabData.disallow = disallow.join(","); - else if (tabData.disallow) - delete tabData.disallow; - - // Save tab attributes. - tabData.attributes = TabAttributes.get(aTab); - - // Store the tab icon. - let tabbrowser = aTab.ownerDocument.defaultView.gBrowser; - tabData.image = tabbrowser.getIcon(aTab); - - if (aTab.__SS_extdata) - tabData.extData = aTab.__SS_extdata; - else if (tabData.extData) - delete tabData.extData; - - if (history && browser.docShell instanceof Ci.nsIDocShell) { - let storageData = SessionStorage.serialize(browser.docShell, aFullData) - if (Object.keys(storageData).length) - tabData.storage = storageData; - } - - return tabData; - }, - - /** - * Get an object that is a serialized representation of a History entry - * Used for data storage - * @param aEntry - * nsISHEntry instance - * @param aFullData - * always return privacy sensitive data (use with care) - * @param aIsPinned - * the tab is pinned and should be treated differently for privacy - * @param aHostSchemeData - * an array of objects with host & scheme keys - * @returns object - */ - _serializeHistoryEntry: - function ssi_serializeHistoryEntry(aEntry, aFullData, aIsPinned, aHostSchemeData) { - var entry = { url: aEntry.URI.spec }; - - try { - // throwing is expensive, we know that about: pages will throw - if (entry.url.indexOf("about:") != 0) - aHostSchemeData.push({ host: aEntry.URI.host, scheme: aEntry.URI.scheme }); - } - catch (ex) { - // We just won't attempt to get cookies for this entry. - } - - if (aEntry.title && aEntry.title != entry.url) { - entry.title = aEntry.title; - } - if (aEntry.isSubFrame) { - entry.subframe = true; - } - if (!(aEntry instanceof Ci.nsISHEntry)) { - return entry; - } - - var cacheKey = aEntry.cacheKey; - if (cacheKey && cacheKey instanceof Ci.nsISupportsPRUint32 && - cacheKey.data != 0) { - // XXXbz would be better to have cache keys implement - // nsISerializable or something. - entry.cacheKey = cacheKey.data; - } - entry.ID = aEntry.ID; - entry.docshellID = aEntry.docshellID; - - if (aEntry.referrerURI) - entry.referrer = aEntry.referrerURI.spec; - - if (aEntry.srcdocData) - entry.srcdocData = aEntry.srcdocData; - - if (aEntry.isSrcdocEntry) - entry.isSrcdocEntry = aEntry.isSrcdocEntry; - - if (aEntry.contentType) - entry.contentType = aEntry.contentType; - - var x = {}, y = {}; - aEntry.getScrollPosition(x, y); - if (x.value != 0 || y.value != 0) - entry.scroll = x.value + "," + y.value; - - try { - var prefPostdata = this._prefBranch.getIntPref("sessionstore.postdata"); - if (aEntry.postData && (aFullData || prefPostdata && - this.checkPrivacyLevel(aEntry.URI.schemeIs("https"), aIsPinned))) { - aEntry.postData.QueryInterface(Ci.nsISeekableStream). - seek(Ci.nsISeekableStream.NS_SEEK_SET, 0); - var stream = Cc["@mozilla.org/binaryinputstream;1"]. - createInstance(Ci.nsIBinaryInputStream); - stream.setInputStream(aEntry.postData); - var postBytes = stream.readByteArray(stream.available()); - var postdata = String.fromCharCode.apply(null, postBytes); - if (aFullData || prefPostdata == -1 || - postdata.replace(/^(Content-.*\r\n)+(\r\n)*/, "").length <= - prefPostdata) { - // We can stop doing base64 encoding once our serialization into JSON - // is guaranteed to handle all chars in strings, including embedded - // nulls. - entry.postdata_b64 = btoa(postdata); - } - } - } - catch (ex) { debug(ex); } // POSTDATA is tricky - especially since some extensions don't get it right - - if (aEntry.triggeringPrincipal) { - // Not catching anything specific here, just possible errors - // from writeCompoundObject and the like. - try { - var binaryStream = Cc["@mozilla.org/binaryoutputstream;1"]. - createInstance(Ci.nsIObjectOutputStream); - var pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe); - pipe.init(false, false, 0, 0xffffffff, null); - binaryStream.setOutputStream(pipe.outputStream); - binaryStream.writeCompoundObject(aEntry.triggeringPrincipal, Ci.nsIPrincipal, true); - binaryStream.close(); - - // Now we want to read the data from the pipe's input end and encode it. - var scriptableStream = Cc["@mozilla.org/binaryinputstream;1"]. - createInstance(Ci.nsIBinaryInputStream); - scriptableStream.setInputStream(pipe.inputStream); - var triggeringPrincipalBytes = - scriptableStream.readByteArray(scriptableStream.available()); - // We can stop doing base64 encoding once our serialization into JSON - // is guaranteed to handle all chars in strings, including embedded - // nulls. - entry.triggeringPrincipal_b64 = btoa(String.fromCharCode.apply(null, triggeringPrincipalBytes)); - } - catch (ex) { debug(ex); } - } - - entry.docIdentifier = aEntry.BFCacheEntry.ID; - - if (aEntry.stateData != null) { - entry.structuredCloneState = aEntry.stateData.getDataAsBase64(); - entry.structuredCloneVersion = aEntry.stateData.formatVersion; - } - - if (!(aEntry instanceof Ci.nsISHContainer)) { - return entry; - } - - if (aEntry.childCount > 0) { - let children = []; - for (var i = 0; i < aEntry.childCount; i++) { - var child = aEntry.GetChildAt(i); - - if (child) { - // don't try to restore framesets containing wyciwyg URLs (cf. bug 424689 and bug 450595) - if (child.URI.schemeIs("wyciwyg")) { - children = []; - break; - } - - children.push(this._serializeHistoryEntry(child, aFullData, - aIsPinned, aHostSchemeData)); - } - } - - if (children.length) - entry.children = children; - } - - return entry; - }, - - /** - * go through all tabs and store the current scroll positions - * and innerHTML content of WYSIWYG editors - * @param aWindow - * Window reference - */ - _updateTextAndScrollData: function ssi_updateTextAndScrollData(aWindow) { - var browsers = aWindow.gBrowser.browsers; - this._windows[aWindow.__SSi].tabs.forEach(function (tabData, i) { - try { - this._updateTextAndScrollDataForTab(aWindow, browsers[i], tabData); - } - catch (ex) { debug(ex); } // get as much data as possible, ignore failures (might succeed the next time) - }, this); - }, - - /** - * go through all frames and store the current scroll positions - * and innerHTML content of WYSIWYG editors - * @param aWindow - * Window reference - * @param aBrowser - * single browser reference - * @param aTabData - * tabData object to add the information to - * @param aFullData - * always return privacy sensitive data (use with care) - */ - _updateTextAndScrollDataForTab: - function ssi_updateTextAndScrollDataForTab(aWindow, aBrowser, aTabData, aFullData) { - // we shouldn't update data for incompletely initialized tabs - if (aBrowser.__SS_data && aBrowser.__SS_tabStillLoading) - return; - - var tabIndex = (aTabData.index || aTabData.entries.length) - 1; - // entry data needn't exist for tabs just initialized with an incomplete session state - if (!aTabData.entries[tabIndex]) - return; - - let selectedPageStyle = aBrowser.markupDocumentViewer.authorStyleDisabled ? "_nostyle" : - this._getSelectedPageStyle(aBrowser.contentWindow); - if (selectedPageStyle) - aTabData.pageStyle = selectedPageStyle; - else if (aTabData.pageStyle) - delete aTabData.pageStyle; - - this._updateTextAndScrollDataForFrame(aWindow, aBrowser.contentWindow, - aTabData.entries[tabIndex], - !aBrowser.__SS_formDataSaved, aFullData, - !!aTabData.pinned); - aBrowser.__SS_formDataSaved = true; - if (aBrowser.currentURI.spec == "about:config") - aTabData.entries[tabIndex].formdata = { - id: { - "textbox": aBrowser.contentDocument.getElementById("textbox").value - }, - xpath: {} - }; - }, - - /** - * go through all subframes and store all form data, the current - * scroll positions and innerHTML content of WYSIWYG editors - * @param aWindow - * Window reference - * @param aContent - * frame reference - * @param aData - * part of a tabData object to add the information to - * @param aUpdateFormData - * update all form data for this tab - * @param aFullData - * always return privacy sensitive data (use with care) - * @param aIsPinned - * the tab is pinned and should be treated differently for privacy - */ - _updateTextAndScrollDataForFrame: - function ssi_updateTextAndScrollDataForFrame(aWindow, aContent, aData, - aUpdateFormData, aFullData, aIsPinned) { - for (var i = 0; i < aContent.frames.length; i++) { - if (aData.children && aData.children[i]) - this._updateTextAndScrollDataForFrame(aWindow, aContent.frames[i], - aData.children[i], aUpdateFormData, - aFullData, aIsPinned); - } - var isHTTPS = this._getURIFromString((aContent.parent || aContent). - document.location.href).schemeIs("https"); - let isAboutSR = aContent.top.document.location.href == "about:sessionrestore"; - if (aFullData || this.checkPrivacyLevel(isHTTPS, aIsPinned) || isAboutSR) { - if (aFullData || aUpdateFormData) { - let formData = DocumentUtils.getFormData(aContent.document); - - // We want to avoid saving data for about:sessionrestore as a string. - // Since it's stored in the form as stringified JSON, stringifying further - // causes an explosion of escape characters. cf. bug 467409 - if (formData && isAboutSR) { - formData.id["sessionData"] = JSON.parse(formData.id["sessionData"]); - } - - if (Object.keys(formData.id).length || - Object.keys(formData.xpath).length) { - aData.formdata = formData; - } else if (aData.formdata) { - delete aData.formdata; - } - } - - // designMode is undefined e.g. for XUL documents (as about:config) - if ((aContent.document.designMode || "") == "on" && aContent.document.body) - aData.innerHTML = aContent.document.body.innerHTML; - } - - // get scroll position from nsIDOMWindowUtils, since it allows avoiding a - // flush of layout - let domWindowUtils = aContent.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - let scrollX = {}, scrollY = {}; - domWindowUtils.getScrollXY(false, scrollX, scrollY); - aData.scroll = scrollX.value + "," + scrollY.value; - }, - - /** - * determine the title of the currently enabled style sheet (if any) - * and recurse through the frameset if necessary - * @param aContent is a frame reference - * @returns the title style sheet determined to be enabled (empty string if none) - */ - _getSelectedPageStyle: function ssi_getSelectedPageStyle(aContent) { - const forScreen = /(?:^|,)\s*(?:all|screen)\s*(?:,|$)/i; - for (let i = 0; i < aContent.document.styleSheets.length; i++) { - let ss = aContent.document.styleSheets[i]; - let media = ss.media.mediaText; - if (!ss.disabled && ss.title && (!media || forScreen.test(media))) - return ss.title - } - for (let i = 0; i < aContent.frames.length; i++) { - let selectedPageStyle = this._getSelectedPageStyle(aContent.frames[i]); - if (selectedPageStyle) - return selectedPageStyle; - } - return ""; - }, - - /** - * extract the base domain from a history entry and its children - * @param aEntry - * the history entry, serialized - * @param aHosts - * the hash that will be used to store hosts eg, { hostname: true } - * @param aCheckPrivacy - * should we check the privacy level for https - * @param aIsPinned - * is the entry we're evaluating for a pinned tab; used only if - * aCheckPrivacy - */ - _extractHostsForCookiesFromEntry: - function ssi_extractHostsForCookiesFromEntry(aEntry, aHosts, aCheckPrivacy, aIsPinned) { - - let host = aEntry._host, - scheme = aEntry._scheme; - - // If host & scheme aren't defined, then we are likely here in the startup - // process via _splitCookiesFromWindow. In that case, we'll turn aEntry.url - // into an nsIURI and get host/scheme from that. This will throw for about: - // urls in which case we don't need to do anything. - if (!host && !scheme) { - try { - let uri = this._getURIFromString(aEntry.url); - host = uri.host; - scheme = uri.scheme; - this._extractHostsForCookiesFromHostScheme(host, scheme, aHosts, aCheckPrivacy, aIsPinned); - } - catch(ex) { } - } - - if (aEntry.children) { - aEntry.children.forEach(function(entry) { - this._extractHostsForCookiesFromEntry(entry, aHosts, aCheckPrivacy, aIsPinned); - }, this); - } - }, - - /** - * extract the base domain from a host & scheme - * @param aHost - * the host of a uri (usually via nsIURI.host) - * @param aScheme - * the scheme of a uri (usually via nsIURI.scheme) - * @param aHosts - * the hash that will be used to store hosts eg, { hostname: true } - * @param aCheckPrivacy - * should we check the privacy level for https - * @param aIsPinned - * is the entry we're evaluating for a pinned tab; used only if - * aCheckPrivacy - */ - _extractHostsForCookiesFromHostScheme: - function ssi_extractHostsForCookiesFromHostScheme(aHost, aScheme, aHosts, aCheckPrivacy, aIsPinned) { - // host and scheme may not be set (for about: urls for example), in which - // case testing scheme will be sufficient. - if (/https?/.test(aScheme) && !aHosts[aHost] && - (!aCheckPrivacy || - this.checkPrivacyLevel(aScheme == "https", aIsPinned))) { - // By setting this to true or false, we can determine when looking at - // the host in _updateCookies if we should check for privacy. - aHosts[aHost] = aIsPinned; - } - else if (aScheme == "file") { - aHosts[aHost] = true; - } - }, - - /** - * store all hosts for a URL - * @param aWindow - * Window reference - */ - _updateCookieHosts: function ssi_updateCookieHosts(aWindow) { - var hosts = this._internalWindows[aWindow.__SSi].hosts = {}; - - // Since _updateCookiesHosts is only ever called for open windows during a - // session, we can call into _extractHostsForCookiesFromHostScheme directly - // using data that is attached to each browser. - for (let i = 0; i < aWindow.gBrowser.tabs.length; i++) { - let tab = aWindow.gBrowser.tabs[i]; - let hostSchemeData = tab.linkedBrowser.__SS_hostSchemeData || []; - for (let j = 0; j < hostSchemeData.length; j++) { - this._extractHostsForCookiesFromHostScheme(hostSchemeData[j].host, - hostSchemeData[j].scheme, - hosts, true, tab.pinned); - } - } - }, - - /** - * Serialize cookie data - * @param aWindows - * JS object containing window data references - * { id: winData, etc. } - */ - _updateCookies: function ssi_updateCookies(aWindows) { - function addCookieToHash(aHash, aHost, aPath, aName, aCookie) { - // lazily build up a 3-dimensional hash, with - // aHost, aPath, and aName as keys - if (!aHash[aHost]) - aHash[aHost] = {}; - if (!aHash[aHost][aPath]) - aHash[aHost][aPath] = {}; - aHash[aHost][aPath][aName] = aCookie; - } - - var jscookies = {}; - var _this = this; - // MAX_EXPIRY should be 2^63-1, but JavaScript can't handle that precision - var MAX_EXPIRY = Math.pow(2, 62); - - for (let [id, window] in Iterator(aWindows)) { - window.cookies = []; - let internalWindow = this._internalWindows[id]; - if (!internalWindow.hosts) - return; - for (var [host, isPinned] in Iterator(internalWindow.hosts)) { - let list; - try { - list = Services.cookies.getCookiesFromHost(host, {}); - } - catch (ex) { - debug("getCookiesFromHost failed. Host: " + host); - } - while (list && list.hasMoreElements()) { - var cookie = list.getNext().QueryInterface(Ci.nsICookie2); - // window._hosts will only have hosts with the right privacy rules, - // so there is no need to do anything special with this call to - // checkPrivacyLevel. - if (cookie.isSession && _this.checkPrivacyLevel(cookie.isSecure, isPinned)) { - // use the cookie's host, path, and name as keys into a hash, - // to make sure we serialize each cookie only once - if (!(cookie.host in jscookies && - cookie.path in jscookies[cookie.host] && - cookie.name in jscookies[cookie.host][cookie.path])) { - var jscookie = { "host": cookie.host, "value": cookie.value }; - // only add attributes with non-default values (saving a few bits) - if (cookie.path) jscookie.path = cookie.path; - if (cookie.name) jscookie.name = cookie.name; - if (cookie.isSecure) jscookie.secure = true; - if (cookie.isHttpOnly) jscookie.httponly = true; - if (cookie.expiry < MAX_EXPIRY) jscookie.expiry = cookie.expiry; - - addCookieToHash(jscookies, cookie.host, cookie.path, cookie.name, jscookie); - } - window.cookies.push(jscookies[cookie.host][cookie.path][cookie.name]); - } - } - } - - // don't include empty cookie sections - if (!window.cookies.length) - delete window.cookies; - } - }, - - /** - * Store window dimensions, visibility, sidebar - * @param aWindow - * Window reference - */ - _updateWindowFeatures: function ssi_updateWindowFeatures(aWindow) { - var winData = this._windows[aWindow.__SSi]; - - WINDOW_ATTRIBUTES.forEach(function(aAttr) { - winData[aAttr] = this._getWindowDimension(aWindow, aAttr); - }, this); - - var hidden = WINDOW_HIDEABLE_FEATURES.filter(function(aItem) { - return aWindow[aItem] && !aWindow[aItem].visible; - }); - if (hidden.length != 0) - winData.hidden = hidden.join(","); - else if (winData.hidden) - delete winData.hidden; - - var sidebar = aWindow.document.getElementById("sidebar-box").getAttribute("sidebarcommand"); - if (sidebar) - winData.sidebar = sidebar; - else if (winData.sidebar) - delete winData.sidebar; - }, - - /** - * gather session data as object - * @param aUpdateAll - * Bool update all windows - * @param aPinnedOnly - * Bool collect pinned tabs only - * @returns object - */ - _getCurrentState: function ssi_getCurrentState(aUpdateAll, aPinnedOnly) { - this._handleClosedWindows(); - - var activeWindow = this._getMostRecentBrowserWindow(); - - if (this._loadState == STATE_RUNNING) { - // update the data for all windows with activities since the last save operation - this._forEachBrowserWindow(function(aWindow) { - if (!this._isWindowLoaded(aWindow)) // window data is still in _statesToRestore - return; - if (aUpdateAll || this._dirtyWindows[aWindow.__SSi] || aWindow == activeWindow) { - this._collectWindowData(aWindow); - } - else { // always update the window features (whose change alone never triggers a save operation) - this._updateWindowFeatures(aWindow); - } - }); - this._dirtyWindows = []; - } - - // collect the data for all windows - var total = [], windows = {}, ids = []; - var nonPopupCount = 0; - var ix; - for (ix in this._windows) { - if (this._windows[ix]._restoring) // window data is still in _statesToRestore - continue; - total.push(this._windows[ix]); - ids.push(ix); - windows[ix] = this._windows[ix]; - if (!this._windows[ix].isPopup) - nonPopupCount++; - } - this._updateCookies(windows); - - // collect the data for all windows yet to be restored - for (ix in this._statesToRestore) { - for each (let winData in this._statesToRestore[ix].windows) { - total.push(winData); - if (!winData.isPopup) - nonPopupCount++; - } - } - - // shallow copy this._closedWindows to preserve current state - let lastClosedWindowsCopy = this._closedWindows.slice(); - -#ifndef XP_MACOSX - // If no non-popup browser window remains open, return the state of the last - // closed window(s). We only want to do this when we're actually "ending" - // the session. - //XXXzpao We should do this for _restoreLastWindow == true, but that has - // its own check for popups. c.f. bug 597619 - if (nonPopupCount == 0 && lastClosedWindowsCopy.length > 0 && - this._loadState == STATE_QUITTING) { - // prepend the last non-popup browser window, so that if the user loads more tabs - // at startup we don't accidentally add them to a popup window - do { - total.unshift(lastClosedWindowsCopy.shift()) - } while (total[0].isPopup && lastClosedWindowsCopy.length > 0) - } -#endif - - if (aPinnedOnly) { - // perform a deep copy so that existing session variables are not changed. - total = JSON.parse(this._toJSONString(total)); - total = total.filter(function (win) { - win.tabs = win.tabs.filter(function (tab) tab.pinned); - // remove closed tabs - win._closedTabs = []; - // correct selected tab index if it was stripped out - if (win.selected > win.tabs.length) - win.selected = 1; - return win.tabs.length > 0; - }); - if (total.length == 0) - return null; - - lastClosedWindowsCopy = []; - } - - if (activeWindow) { - this.activeWindowSSiCache = activeWindow.__SSi || ""; - } - ix = ids.indexOf(this.activeWindowSSiCache); - // We don't want to restore focus to a minimized window or a window which had all its - // tabs stripped out (doesn't exist). - if (ix != -1 && total[ix] && total[ix].sizemode == "minimized") - ix = -1; - - let session = { - state: this._loadState == STATE_RUNNING ? STATE_RUNNING_STR : STATE_STOPPED_STR, - lastUpdate: Date.now(), - startTime: this._sessionStartTime, - recentCrashes: this._recentCrashes - }; - - var scratchpads = null; - var browserConsole = null; -#ifdef MOZ_DEVTOOLS - // Scratchpad - // get open Scratchpad window states too - scratchpads = ScratchpadManager.getSessionState(); - - // The Browser Console - browserConsole = HUDService.getBrowserConsoleSessionState(); -#endif - - return { - windows: total, - selectedWindow: ix + 1, - _closedWindows: lastClosedWindowsCopy, -#ifdef MOZ_DEVTOOLS - session: session, - scratchpads: scratchpads, - browserConsole: browserConsole -#else - session: session -#endif - }; - }, - - /** - * serialize session data for a window - * @param aWindow - * Window reference - * @returns string - */ - _getWindowState: function ssi_getWindowState(aWindow) { - if (!this._isWindowLoaded(aWindow)) - return this._statesToRestore[aWindow.__SS_restoreID]; - - if (this._loadState == STATE_RUNNING) { - this._collectWindowData(aWindow); - } - - var winData = this._windows[aWindow.__SSi]; - let windows = {}; - windows[aWindow.__SSi] = winData; - this._updateCookies(windows); - - return { windows: [winData] }; - }, - - _collectWindowData: function ssi_collectWindowData(aWindow) { - if (!this._isWindowLoaded(aWindow)) - return; - - // update the internal state data for this window - this._saveWindowHistory(aWindow); - this._updateTextAndScrollData(aWindow); - this._updateCookieHosts(aWindow); - this._updateWindowFeatures(aWindow); - - // Make sure we keep __SS_lastSessionWindowID around for cases like entering - // or leaving PB mode. - if (aWindow.__SS_lastSessionWindowID) - this._windows[aWindow.__SSi].__lastSessionWindowID = - aWindow.__SS_lastSessionWindowID; - - this._dirtyWindows[aWindow.__SSi] = false; - }, - - /* ........ Restoring Functionality .............. */ - - /** - * restore features to a single window - * @param aWindow - * Window reference - * @param aState - * JS object or its eval'able source - * @param aOverwriteTabs - * bool overwrite existing tabs w/ new ones - * @param aFollowUp - * bool this isn't the restoration of the first window - */ - restoreWindow: function ssi_restoreWindow(aWindow, aState, aOverwriteTabs, aFollowUp) { - if (!aFollowUp) { - this.windowToFocus = aWindow; - } - // initialize window if necessary - if (aWindow && (!aWindow.__SSi || !this._windows[aWindow.__SSi])) - this.onLoad(aWindow); - - try { - var root = typeof aState == "string" ? JSON.parse(aState) : aState; - if (!root.windows[0]) { - this._sendRestoreCompletedNotifications(); - return; // nothing to restore - } - } - catch (ex) { // invalid state object - don't restore anything - debug(ex); - this._sendRestoreCompletedNotifications(); - return; - } - - // We're not returning from this before we end up calling restoreHistoryPrecursor - // for this window, so make sure we send the SSWindowStateBusy event. - this._setWindowStateBusy(aWindow); - - if (root._closedWindows) - this._closedWindows = root._closedWindows; - - var winData; - if (!root.selectedWindow || root.selectedWindow > root.windows.length) { - root.selectedWindow = 0; - } - - // open new windows for all further window entries of a multi-window session - // (unless they don't contain any tab data) - for (var w = 1; w < root.windows.length; w++) { - winData = root.windows[w]; - if (winData && winData.tabs && winData.tabs[0]) { - var window = this._openWindowWithState({ windows: [winData] }); - if (w == root.selectedWindow - 1) { - this.windowToFocus = window; - } - } - } - winData = root.windows[0]; - if (!winData.tabs) { - winData.tabs = []; - } - // don't restore a single blank tab when we've had an external - // URL passed in for loading at startup (cf. bug 357419) - else if (root._firstTabs && !aOverwriteTabs && winData.tabs.length == 1 && - (!winData.tabs[0].entries || winData.tabs[0].entries.length == 0)) { - winData.tabs = []; - } - - var tabbrowser = aWindow.gBrowser; - var openTabCount = aOverwriteTabs ? tabbrowser.browsers.length : -1; - var newTabCount = winData.tabs.length; - var tabs = []; - - // disable smooth scrolling while adding, moving, removing and selecting tabs - var tabstrip = tabbrowser.tabContainer.mTabstrip; - var smoothScroll = tabstrip.smoothScroll; - tabstrip.smoothScroll = false; - - // unpin all tabs to ensure they are not reordered in the next loop - if (aOverwriteTabs) { - for (let t = tabbrowser._numPinnedTabs - 1; t > -1; t--) - tabbrowser.unpinTab(tabbrowser.tabs[t]); - } - - // make sure that the selected tab won't be closed in order to - // prevent unnecessary flickering - if (aOverwriteTabs && tabbrowser.selectedTab._tPos >= newTabCount) - tabbrowser.moveTabTo(tabbrowser.selectedTab, newTabCount - 1); - - let numVisibleTabs = 0; - - for (var t = 0; t < newTabCount; t++) { - tabs.push(t < openTabCount ? - tabbrowser.tabs[t] : - tabbrowser.addTab("about:blank", - {skipAnimation: true, - skipBackgroundNotify: true})); - // when resuming at startup: add additionally requested pages to the end - if (!aOverwriteTabs && root._firstTabs) { - tabbrowser.moveTabTo(tabs[t], t); - } - - if (winData.tabs[t].pinned) - tabbrowser.pinTab(tabs[t]); - - if (winData.tabs[t].hidden) { - tabbrowser.hideTab(tabs[t]); - } - else { - tabbrowser.showTab(tabs[t]); - numVisibleTabs++; - } - } - - // if all tabs to be restored are hidden, make the first one visible - if (!numVisibleTabs && winData.tabs.length) { - winData.tabs[0].hidden = false; - tabbrowser.showTab(tabs[0]); - } - - // If overwriting tabs, we want to reset each tab's "restoring" state. Since - // we're overwriting those tabs, they should no longer be restoring. The - // tabs will be rebuilt and marked if they need to be restored after loading - // state (in restoreHistoryPrecursor). - if (aOverwriteTabs) { - for (let i = 0; i < tabbrowser.tabs.length; i++) { - if (tabbrowser.browsers[i].__SS_restoreState) - this._resetTabRestoringState(tabbrowser.tabs[i]); - } - } - - // We want to set up a counter on the window that indicates how many tabs - // in this window are unrestored. This will be used in restoreNextTab to - // determine if gRestoreTabsProgressListener should be removed from the window. - // If we aren't overwriting existing tabs, then we want to add to the existing - // count in case there are still tabs restoring. - if (!aWindow.__SS_tabsToRestore) - aWindow.__SS_tabsToRestore = 0; - if (aOverwriteTabs) - aWindow.__SS_tabsToRestore = newTabCount; - else - aWindow.__SS_tabsToRestore += newTabCount; - - // We want to correlate the window with data from the last session, so - // assign another id if we have one. Otherwise clear so we don't do - // anything with it. - delete aWindow.__SS_lastSessionWindowID; - if (winData.__lastSessionWindowID) - aWindow.__SS_lastSessionWindowID = winData.__lastSessionWindowID; - - // when overwriting tabs, remove all superflous ones - if (aOverwriteTabs && newTabCount < openTabCount) { - Array.slice(tabbrowser.tabs, newTabCount, openTabCount) - .forEach(tabbrowser.removeTab, tabbrowser); - } - - if (aOverwriteTabs) { - this.restoreWindowFeatures(aWindow, winData); - delete this._windows[aWindow.__SSi].extData; - } - if (winData.cookies) { - this.restoreCookies(winData.cookies); - } - if (winData.extData) { - if (!this._windows[aWindow.__SSi].extData) { - this._windows[aWindow.__SSi].extData = {}; - } - for (var key in winData.extData) { - this._windows[aWindow.__SSi].extData[key] = winData.extData[key]; - } - } - if (aOverwriteTabs || root._firstTabs) { - this._windows[aWindow.__SSi]._closedTabs = winData._closedTabs || []; - } - - this.restoreHistoryPrecursor(aWindow, tabs, winData.tabs, - (aOverwriteTabs ? (parseInt(winData.selected) || 1) : 0), 0, 0); - -#ifdef MOZ_DEVTOOLS - if (aState.scratchpads) { - ScratchpadManager.restoreSession(aState.scratchpads); - } - - // The Browser Console - if (aState.browserConsole) { - HUDService.restoreBrowserConsoleSession(); - } - -#endif - // set smoothScroll back to the original value - tabstrip.smoothScroll = smoothScroll; - - this._sendRestoreCompletedNotifications(); - }, - - /** - * Sets the tabs restoring order with the following priority: - * Selected tab, pinned tabs, optimized visible tabs, other visible tabs and - * hidden tabs. - * @param aTabBrowser - * Tab browser object - * @param aTabs - * Array of tab references - * @param aTabData - * Array of tab data - * @param aSelectedTab - * Index of selected tab (1 is first tab, 0 no selected tab) - */ - _setTabsRestoringOrder : function ssi__setTabsRestoringOrder( - aTabBrowser, aTabs, aTabData, aSelectedTab) { - - // Store the selected tab. Need to substract one to get the index in aTabs. - let selectedTab; - if (aSelectedTab > 0 && aTabs[aSelectedTab - 1]) { - selectedTab = aTabs[aSelectedTab - 1]; - } - - // Store the pinned tabs and hidden tabs. - let pinnedTabs = []; - let pinnedTabsData = []; - let hiddenTabs = []; - let hiddenTabsData = []; - if (aTabs.length > 1) { - for (let t = aTabs.length - 1; t >= 0; t--) { - if (aTabData[t].pinned) { - pinnedTabs.unshift(aTabs.splice(t, 1)[0]); - pinnedTabsData.unshift(aTabData.splice(t, 1)[0]); - } else if (aTabData[t].hidden) { - hiddenTabs.unshift(aTabs.splice(t, 1)[0]); - hiddenTabsData.unshift(aTabData.splice(t, 1)[0]); - } - } - } - - // Optimize the visible tabs only if there is a selected tab. - if (selectedTab) { - let selectedTabIndex = aTabs.indexOf(selectedTab); - if (selectedTabIndex > 0) { - let scrollSize = aTabBrowser.tabContainer.mTabstrip.scrollClientSize; - let tabWidth = aTabs[0].getBoundingClientRect().width; - let maxVisibleTabs = Math.ceil(scrollSize / tabWidth); - if (maxVisibleTabs < aTabs.length) { - let firstVisibleTab = 0; - let nonVisibleTabsCount = aTabs.length - maxVisibleTabs; - if (nonVisibleTabsCount >= selectedTabIndex) { - // Selected tab is leftmost since we scroll to it when possible. - firstVisibleTab = selectedTabIndex; - } else { - // Selected tab is rightmost or no more room to scroll right. - firstVisibleTab = nonVisibleTabsCount; - } - aTabs = aTabs.splice(firstVisibleTab, maxVisibleTabs).concat(aTabs); - aTabData = - aTabData.splice(firstVisibleTab, maxVisibleTabs).concat(aTabData); - } - } - } - - // Merge the stored tabs in order. - aTabs = pinnedTabs.concat(aTabs, hiddenTabs); - aTabData = pinnedTabsData.concat(aTabData, hiddenTabsData); - - // Load the selected tab to the first position and select it. - if (selectedTab) { - let selectedTabIndex = aTabs.indexOf(selectedTab); - if (selectedTabIndex > 0) { - aTabs = aTabs.splice(selectedTabIndex, 1).concat(aTabs); - aTabData = aTabData.splice(selectedTabIndex, 1).concat(aTabData); - } - aTabBrowser.selectedTab = selectedTab; - } - - return [aTabs, aTabData]; - }, - - /** - * Manage history restoration for a window - * @param aWindow - * Window to restore the tabs into - * @param aTabs - * Array of tab references - * @param aTabData - * Array of tab data - * @param aSelectTab - * Index of selected tab - * @param aIx - * Index of the next tab to check readyness for - * @param aCount - * Counter for number of times delaying b/c browser or history aren't ready - * @param aRestoreImmediately - * Flag to indicate whether the given set of tabs aTabs should be - * restored/loaded immediately even if restore_on_demand = true - */ - restoreHistoryPrecursor: - function ssi_restoreHistoryPrecursor(aWindow, aTabs, aTabData, aSelectTab, - aIx, aCount, aRestoreImmediately = false) { - var tabbrowser = aWindow.gBrowser; - - // make sure that all browsers and their histories are available - // - if one's not, resume this check in 100ms (repeat at most 10 times) - for (var t = aIx; t < aTabs.length; t++) { - try { - if (!tabbrowser.getBrowserForTab(aTabs[t]).webNavigation.sessionHistory) { - throw new Error(); - } - } - catch (ex) { // in case browser or history aren't ready yet - if (aCount < 10) { - var restoreHistoryFunc = function(self) { - self.restoreHistoryPrecursor(aWindow, aTabs, aTabData, aSelectTab, - aIx, aCount + 1, aRestoreImmediately); - } - aWindow.setTimeout(restoreHistoryFunc, 100, this); - return; - } - } - } - - if (!this._isWindowLoaded(aWindow)) { - // from now on, the data will come from the actual window - delete this._statesToRestore[aWindow.__SS_restoreID]; - delete aWindow.__SS_restoreID; - delete this._windows[aWindow.__SSi]._restoring; - - // It's important to set the window state to dirty so that - // we collect their data for the first time when saving state. - this._dirtyWindows[aWindow.__SSi] = true; - } - - if (aTabs.length == 0) { - // this is normally done in restoreHistory() but as we're returning early - // here we need to take care of it. - this._setWindowStateReady(aWindow); - return; - } - - // Sets the tabs restoring order. - [aTabs, aTabData] = - this._setTabsRestoringOrder(tabbrowser, aTabs, aTabData, aSelectTab); - - // Prepare the tabs so that they can be properly restored. We'll pin/unpin - // and show/hide tabs as necessary. We'll also set the labels, user typed - // value, and attach a copy of the tab's data in case we close it before - // it's been restored. - for (t = 0; t < aTabs.length; t++) { - let tab = aTabs[t]; - let browser = tabbrowser.getBrowserForTab(tab); - let tabData = aTabData[t]; - - if (tabData.pinned) - tabbrowser.pinTab(tab); - else - tabbrowser.unpinTab(tab); - - if (tabData.hidden) - tabbrowser.hideTab(tab); - else - tabbrowser.showTab(tab); - - if ("attributes" in tabData) { - // Ensure that we persist tab attributes restored from previous sessions. - Object.keys(tabData.attributes).forEach(a => TabAttributes.persist(a)); - } - - browser.__SS_tabStillLoading = true; - - // keep the data around to prevent dataloss in case - // a tab gets closed before it's been properly restored - browser.__SS_data = tabData; - browser.__SS_restoreState = TAB_STATE_NEEDS_RESTORE; - browser.setAttribute("pending", "true"); - tab.setAttribute("pending", "true"); - - // Make sure that set/getTabValue will set/read the correct data by - // wiping out any current value in tab.__SS_extdata. - delete tab.__SS_extdata; - - if (!tabData.entries || tabData.entries.length == 0) { - // make sure to blank out this tab's content - // (just purging the tab's history won't be enough) - browser.contentDocument.location = "about:blank"; - continue; - } - - browser.stop(); // in case about:blank isn't done yet - - // wall-paper fix for bug 439675: make sure that the URL to be loaded - // is always visible in the address bar - let activeIndex = (tabData.index || tabData.entries.length) - 1; - let activePageData = tabData.entries[activeIndex] || null; - let uri = activePageData ? activePageData.url || null : null; - browser.userTypedValue = uri; - - // Also make sure currentURI is set so that switch-to-tab works before - // the tab is restored. We'll reset this to about:blank when we try to - // restore the tab to ensure that docshell doeesn't get confused. - if (uri) - browser.docShell.setCurrentURI(this._getURIFromString(uri)); - - // If the page has a title, set it. - if (activePageData) { - if (activePageData.title) { - tab.label = activePageData.title; - tab.crop = "end"; - } else if (activePageData.url != "about:blank") { - tab.label = activePageData.url; - tab.crop = "center"; - } - } - } - - // helper hashes for ensuring unique frame IDs and unique document - // identifiers. - var idMap = { used: {} }; - var docIdentMap = {}; - this.restoreHistory(aWindow, aTabs, aTabData, idMap, docIdentMap, - aRestoreImmediately); - }, - - /** - * Restore history for a window - * @param aWindow - * Window reference - * @param aTabs - * Array of tab references - * @param aTabData - * Array of tab data - * @param aIdMap - * Hash for ensuring unique frame IDs - * @param aRestoreImmediately - * Flag to indicate whether the given set of tabs aTabs should be - * restored/loaded immediately even if restore_on_demand = true - */ - restoreHistory: - function ssi_restoreHistory(aWindow, aTabs, aTabData, aIdMap, aDocIdentMap, - aRestoreImmediately) { - var _this = this; - // if the tab got removed before being completely restored, then skip it - while (aTabs.length > 0 && !(this._canRestoreTabHistory(aTabs[0]))) { - aTabs.shift(); - aTabData.shift(); - } - if (aTabs.length == 0) { - // At this point we're essentially ready for consumers to read/write data - // via the sessionstore API so we'll send the SSWindowStateReady event. - this._setWindowStateReady(aWindow); - return; // no more tabs to restore - } - - var tab = aTabs.shift(); - var tabData = aTabData.shift(); - - var browser = aWindow.gBrowser.getBrowserForTab(tab); - var history = browser.webNavigation.sessionHistory; - - if (history.count > 0) { - history.PurgeHistory(history.count); - } - history.QueryInterface(Ci.nsISHistoryInternal); - - browser.__SS_shistoryListener = new SessionStoreSHistoryListener(tab); - history.addSHistoryListener(browser.__SS_shistoryListener); - - if (!tabData.entries) { - tabData.entries = []; - } - if (tabData.extData) { - tab.__SS_extdata = {}; - for (let key in tabData.extData) - tab.__SS_extdata[key] = tabData.extData[key]; - } - else - delete tab.__SS_extdata; - - for (var i = 0; i < tabData.entries.length; i++) { - //XXXzpao Wallpaper patch for bug 514751 - if (!tabData.entries[i].url) - continue; - history.addEntry(this._deserializeHistoryEntry(tabData.entries[i], - aIdMap, aDocIdentMap), true); - } - - // make sure to reset the capabilities and attributes, in case this tab gets reused - let disallow = new Set(tabData.disallow && tabData.disallow.split(",")); - for (let cap of gDocShellCapabilities(browser.docShell)) - browser.docShell["allow" + cap] = !disallow.has(cap); - - // Restore tab attributes. - if ("attributes" in tabData) { - TabAttributes.set(tab, tabData.attributes); - } - - // Restore the tab icon. - if ("image" in tabData) { - // Using null as the loadingPrincipal because serializing - // the principal would be overkill. Within SetIcon we - // default to the systemPrincipal if aLoadingPrincipal is - // null which will allow the favicon to load. - aWindow.gBrowser.setIcon(tab, tabData.image, null); - } - - if (tabData.storage && browser.docShell instanceof Ci.nsIDocShell) - SessionStorage.deserialize(browser.docShell, tabData.storage); - - // notify the tabbrowser that the tab chrome has been restored - var event = aWindow.document.createEvent("Events"); - event.initEvent("SSTabRestoring", true, false); - tab.dispatchEvent(event); - - // Restore the history in the next tab - aWindow.setTimeout(function(){ - _this.restoreHistory(aWindow, aTabs, aTabData, aIdMap, aDocIdentMap, - aRestoreImmediately); - }, 0); - - // This could cause us to ignore max_concurrent_tabs pref a bit, but - // it ensures each window will have its selected tab loaded. - if (aRestoreImmediately || aWindow.gBrowser.selectedBrowser == browser) { - this.restoreTab(tab); - } - else { - TabRestoreQueue.add(tab); - this.restoreNextTab(); - } - }, - - /** - * Restores the specified tab. If the tab can't be restored (eg, no history or - * calling gotoIndex fails), then state changes will be rolled back. - * This method will check if gTabsProgressListener is attached to the tab's - * window, ensuring that we don't get caught without one. - * This method removes the session history listener right before starting to - * attempt a load. This will prevent cases of "stuck" listeners. - * If this method returns false, then it is up to the caller to decide what to - * do. In the common case (restoreNextTab), we will want to then attempt to - * restore the next tab. In the other case (selecting the tab, reloading the - * tab), the caller doesn't actually want to do anything if no page is loaded. - * - * @param aTab - * the tab to restore - * - * @returns true/false indicating whether or not a load actually happened - */ - restoreTab: function ssi_restoreTab(aTab) { - let window = aTab.ownerDocument.defaultView; - let browser = aTab.linkedBrowser; - let tabData = browser.__SS_data; - - // There are cases within where we haven't actually started a load. In that - // that case we'll reset state changes we made and return false to the caller - // can handle appropriately. - let didStartLoad = false; - - // Make sure that the tabs progress listener is attached to this window - this._ensureTabsProgressListener(window); - - // Make sure that this tab is removed from the priority queue. - TabRestoreQueue.remove(aTab); - - // Increase our internal count. - this._tabsRestoringCount++; - - // Set this tab's state to restoring - browser.__SS_restoreState = TAB_STATE_RESTORING; - browser.removeAttribute("pending"); - aTab.removeAttribute("pending"); - - // Remove the history listener, since we no longer need it once we start restoring - this._removeSHistoryListener(aTab); - - let activeIndex = (tabData.index || tabData.entries.length) - 1; - if (activeIndex >= tabData.entries.length) - activeIndex = tabData.entries.length - 1; - // Reset currentURI. This creates a new session history entry with a new - // doc identifier, so we need to explicitly save and restore the old doc - // identifier (corresponding to the SHEntry at activeIndex) below. - browser.webNavigation.setCurrentURI(this._getURIFromString("about:blank")); - // Attach data that will be restored on "load" event, after tab is restored. - if (activeIndex > -1) { - // restore those aspects of the currently active documents which are not - // preserved in the plain history entries (mainly scroll state and text data) - browser.__SS_restore_data = tabData.entries[activeIndex] || {}; - browser.__SS_restore_pageStyle = tabData.pageStyle || ""; - browser.__SS_restore_tab = aTab; - didStartLoad = true; - try { - // In order to work around certain issues in session history, we need to - // force session history to update its internal index and call reload - // instead of gotoIndex. See bug 597315. - browser.webNavigation.sessionHistory.getEntryAtIndex(activeIndex, true); - browser.webNavigation.sessionHistory.reloadCurrentEntry(); - // If the user prefers it, bypass cache and always load from the network. - let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE; - switch (this._cacheBehavior) { - case 2: // hard refresh - flags = Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | - Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE; - browser.webNavigation.reload(flags); - break; - case 1: // soft refresh - browser.webNavigation.reload(flags); - break; - default: // 0 or other: use cache, so do nothing. - break; - } - } - catch (ex) { - // ignore page load errors - aTab.removeAttribute("busy"); - didStartLoad = false; - } - } - - // Handle userTypedValue. Setting userTypedValue seems to update gURLbar - // as needed. Calling loadURI will cancel form filling in restoreDocument - if (tabData.userTypedValue) { - browser.userTypedValue = tabData.userTypedValue; - if (tabData.userTypedClear) { - // Make it so that we'll enter restoreDocument on page load. We will - // fire SSTabRestored from there. We don't have any form data to restore - // so we can just set the URL to null. - browser.__SS_restore_data = { url: null }; - browser.__SS_restore_tab = aTab; - if (didStartLoad) - browser.stop(); - didStartLoad = true; - browser.loadURIWithFlags(tabData.userTypedValue, - Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP); - } - } - - // If we didn't start a load, then we won't reset this tab through the usual - // channel (via the progress listener), so reset the tab ourselves. We will - // also send SSTabRestored since this tab has technically been restored. - if (!didStartLoad) { - this._sendTabRestoredNotification(aTab); - this._resetTabRestoringState(aTab); - } - - return didStartLoad; - }, - - /** - * This _attempts_ to restore the next available tab. If the restore fails, - * then we will attempt the next one. - * There are conditions where this won't do anything: - * if we're in the process of quitting - * if there are no tabs to restore - * if we have already reached the limit for number of tabs to restore - */ - restoreNextTab: function ssi_restoreNextTab() { - // If we call in here while quitting, we don't actually want to do anything - if (this._loadState == STATE_QUITTING) - return; - - // Don't exceed the maximum number of concurrent tab restores. - if (this._tabsRestoringCount >= this._maxConcurrentTabRestores) - return; - - let tab = TabRestoreQueue.shift(); - if (tab) { - let didStartLoad = this.restoreTab(tab); - // If we don't start a load in the restored tab (eg, no entries) then we - // want to attempt to restore the next tab. - if (!didStartLoad) - this.restoreNextTab(); - } - }, - - /** - * expands serialized history data into a session-history-entry instance - * @param aEntry - * Object containing serialized history data for a URL - * @param aIdMap - * Hash for ensuring unique frame IDs - * @returns nsISHEntry - */ - _deserializeHistoryEntry: - function ssi_deserializeHistoryEntry(aEntry, aIdMap, aDocIdentMap) { - - var shEntry = Cc["@mozilla.org/browser/session-history-entry;1"]. - createInstance(Ci.nsISHEntry); - - shEntry.setURI(this._getURIFromString(aEntry.url)); - shEntry.setTitle(aEntry.title || aEntry.url); - if (aEntry.subframe) - shEntry.setIsSubFrame(aEntry.subframe || false); - shEntry.loadType = Ci.nsIDocShellLoadInfo.loadHistory; - if (aEntry.contentType) - shEntry.contentType = aEntry.contentType; - if (aEntry.referrer) - shEntry.referrerURI = this._getURIFromString(aEntry.referrer); - if (aEntry.isSrcdocEntry) - shEntry.srcdocData = aEntry.srcdocData; - - if (aEntry.cacheKey) { - var cacheKey = Cc["@mozilla.org/supports-PRUint32;1"]. - createInstance(Ci.nsISupportsPRUint32); - cacheKey.data = aEntry.cacheKey; - shEntry.cacheKey = cacheKey; - } - - if (aEntry.ID) { - // get a new unique ID for this frame (since the one from the last - // start might already be in use) - var id = aIdMap[aEntry.ID] || 0; - if (!id) { - for (id = Date.now(); id in aIdMap.used; id++); - aIdMap[aEntry.ID] = id; - aIdMap.used[id] = true; - } - shEntry.ID = id; - } - - if (aEntry.docshellID) - shEntry.docshellID = aEntry.docshellID; - - if (aEntry.structuredCloneState && aEntry.structuredCloneVersion) { - shEntry.stateData = - Cc["@mozilla.org/docshell/structured-clone-container;1"]. - createInstance(Ci.nsIStructuredCloneContainer); - - shEntry.stateData.initFromBase64(aEntry.structuredCloneState, - aEntry.structuredCloneVersion); - } - - if (aEntry.scroll) { - var scrollPos = (aEntry.scroll || "0,0").split(","); - scrollPos = [parseInt(scrollPos[0]) || 0, parseInt(scrollPos[1]) || 0]; - shEntry.setScrollPosition(scrollPos[0], scrollPos[1]); - } - - if (aEntry.postdata_b64) { - var postdata = atob(aEntry.postdata_b64); - var stream = Cc["@mozilla.org/io/string-input-stream;1"]. - createInstance(Ci.nsIStringInputStream); - stream.setData(postdata, postdata.length); - shEntry.postData = stream; - } - - let childDocIdents = {}; - if (aEntry.docIdentifier) { - // If we have a serialized document identifier, try to find an SHEntry - // which matches that doc identifier and adopt that SHEntry's - // BFCacheEntry. If we don't find a match, insert shEntry as the match - // for the document identifier. - let matchingEntry = aDocIdentMap[aEntry.docIdentifier]; - if (!matchingEntry) { - matchingEntry = {shEntry: shEntry, childDocIdents: childDocIdents}; - aDocIdentMap[aEntry.docIdentifier] = matchingEntry; - } - else { - shEntry.adoptBFCacheEntry(matchingEntry.shEntry); - childDocIdents = matchingEntry.childDocIdents; - } - } - - // The field aEntry.owner_b64 got renamed to aEntry.triggeringPricipal_b64 in - // Bug 1286472. To remain backward compatible we still have to support that - // field for a few cycles before we can remove it within Bug 1289785. - if (aEntry.owner_b64) { - aEntry.triggeringPrincipal_b64 = aEntry.owner_b64; - delete aEntry.owner_b64; - } - - if (aEntry.triggeringPrincipal_b64) { - var triggeringPrincipalInput = Cc["@mozilla.org/io/string-input-stream;1"]. - createInstance(Ci.nsIStringInputStream); - var binaryData = atob(aEntry.triggeringPrincipal_b64); - triggeringPrincipalInput.setData(binaryData, binaryData.length); - var binaryStream = Cc["@mozilla.org/binaryinputstream;1"]. - createInstance(Ci.nsIObjectInputStream); - binaryStream.setInputStream(triggeringPrincipalInput); - try { // Catch possible deserialization exceptions - shEntry.triggeringPrincipal = binaryStream.readObject(true); - } catch (ex) { debug(ex); } - } - - if (aEntry.children && shEntry instanceof Ci.nsISHContainer) { - for (var i = 0; i < aEntry.children.length; i++) { - //XXXzpao Wallpaper patch for bug 514751 - if (!aEntry.children[i].url) - continue; - - // We're getting sessionrestore.js files with a cycle in the - // doc-identifier graph, likely due to bug 698656. (That is, we have - // an entry where doc identifier A is an ancestor of doc identifier B, - // and another entry where doc identifier B is an ancestor of A.) - // - // If we were to respect these doc identifiers, we'd create a cycle in - // the SHEntries themselves, which causes the docshell to loop forever - // when it looks for the root SHEntry. - // - // So as a hack to fix this, we restrict the scope of a doc identifier - // to be a node's siblings and cousins, and pass childDocIdents, not - // aDocIdents, to _deserializeHistoryEntry. That is, we say that two - // SHEntries with the same doc identifier have the same document iff - // they have the same parent or their parents have the same document. - - shEntry.AddChild(this._deserializeHistoryEntry(aEntry.children[i], aIdMap, - childDocIdents), i); - } - } - - return shEntry; - }, - - /** - * Restore properties to a loaded document - */ - restoreDocument: function ssi_restoreDocument(aWindow, aBrowser, aEvent) { - // wait for the top frame to be loaded completely - if (!aEvent || !aEvent.originalTarget || !aEvent.originalTarget.defaultView || - aEvent.originalTarget.defaultView != aEvent.originalTarget.defaultView.top) { - return; - } - - // always call this before injecting content into a document! - function hasExpectedURL(aDocument, aURL) - !aURL || aURL.replace(/#.*/, "") == aDocument.location.href.replace(/#.*/, ""); - - let selectedPageStyle = aBrowser.__SS_restore_pageStyle; - function restoreTextDataAndScrolling(aContent, aData, aPrefix) { - if (aData.formdata && hasExpectedURL(aContent.document, aData.url)) { - let formdata = aData.formdata; - - // handle backwards compatibility - // this is a migration from pre-firefox 15. cf. bug 742051 - if (!("xpath" in formdata || "id" in formdata)) { - formdata = { xpath: {}, id: {} }; - - for each (let [key, value] in Iterator(aData.formdata)) { - if (key.charAt(0) == "#") { - formdata.id[key.slice(1)] = value; - } else { - formdata.xpath[key] = value; - } - } - } - - // for about:sessionrestore we saved the field as JSON to avoid - // nested instances causing humongous sessionstore.js files. - // cf. bug 467409 - if (aData.url == "about:sessionrestore" && - "sessionData" in formdata.id && - typeof formdata.id["sessionData"] == "object") { - formdata.id["sessionData"] = - JSON.stringify(formdata.id["sessionData"]); - } - - // update the formdata - aData.formdata = formdata; - // merge the formdata - DocumentUtils.mergeFormData(aContent.document, formdata); - } - - if (aData.innerHTML) { - aWindow.setTimeout(function() { - if (aContent.document.designMode == "on" && - hasExpectedURL(aContent.document, aData.url) && - aContent.document.body) { - aContent.document.body.innerHTML = aData.innerHTML; - } - }, 0); - } - var match; - if (aData.scroll && (match = /(\d+),(\d+)/.exec(aData.scroll)) != null) { - aContent.scrollTo(match[1], match[2]); - } - Array.forEach(aContent.document.styleSheets, function(aSS) { - aSS.disabled = aSS.title && aSS.title != selectedPageStyle; - }); - for (var i = 0; i < aContent.frames.length; i++) { - if (aData.children && aData.children[i] && - hasExpectedURL(aContent.document, aData.url)) { - restoreTextDataAndScrolling(aContent.frames[i], aData.children[i], aPrefix + i + "|"); - } - } - } - - // don't restore text data and scrolling state if the user has navigated - // away before the loading completed (except for in-page navigation) - if (hasExpectedURL(aEvent.originalTarget, aBrowser.__SS_restore_data.url)) { - var content = aEvent.originalTarget.defaultView; - restoreTextDataAndScrolling(content, aBrowser.__SS_restore_data, ""); - aBrowser.markupDocumentViewer.authorStyleDisabled = selectedPageStyle == "_nostyle"; - } - - // notify the tabbrowser that this document has been completely restored - this._sendTabRestoredNotification(aBrowser.__SS_restore_tab); - - delete aBrowser.__SS_restore_data; - delete aBrowser.__SS_restore_pageStyle; - delete aBrowser.__SS_restore_tab; - }, - - /** - * Restore visibility and dimension features to a window - * @param aWindow - * Window reference - * @param aWinData - * Object containing session data for the window - */ - restoreWindowFeatures: function ssi_restoreWindowFeatures(aWindow, aWinData) { - var hidden = (aWinData.hidden)?aWinData.hidden.split(","):[]; - WINDOW_HIDEABLE_FEATURES.forEach(function(aItem) { - aWindow[aItem].visible = hidden.indexOf(aItem) == -1; - }); - - if (aWinData.isPopup) { - this._windows[aWindow.__SSi].isPopup = true; - if (aWindow.gURLBar) { - aWindow.gURLBar.readOnly = true; - aWindow.gURLBar.setAttribute("enablehistory", "false"); - } - } - else { - delete this._windows[aWindow.__SSi].isPopup; - if (aWindow.gURLBar) { - aWindow.gURLBar.readOnly = false; - aWindow.gURLBar.setAttribute("enablehistory", "true"); - } - } - - var _this = this; - aWindow.setTimeout(function() { - _this.restoreDimensions.apply(_this, [aWindow, - +aWinData.width || 0, - +aWinData.height || 0, - "screenX" in aWinData ? +aWinData.screenX : NaN, - "screenY" in aWinData ? +aWinData.screenY : NaN, - aWinData.sizemode || "", aWinData.sidebar || ""]); - }, 0); - }, - - /** - * Restore a window's dimensions - * @param aWidth - * Window width - * @param aHeight - * Window height - * @param aLeft - * Window left - * @param aTop - * Window top - * @param aSizeMode - * Window size mode (eg: maximized) - * @param aSidebar - * Sidebar command - */ - restoreDimensions: function ssi_restoreDimensions(aWindow, aWidth, aHeight, aLeft, aTop, aSizeMode, aSidebar) { - var win = aWindow; - var _this = this; - function win_(aName) { return _this._getWindowDimension(win, aName); } - - // Find available space on the screen where this window is being placed - let screen = gScreenManager.screenForRect(aLeft, aTop, aWidth, aHeight); - if (screen && !this._prefBranch.getBoolPref("sessionstore.exactPos")) { - let screenLeft = {}, screenTop = {}, screenWidth = {}, screenHeight = {}; - screen.GetAvailRectDisplayPix(screenLeft, screenTop, screenWidth, screenHeight); - - // Screen X/Y are based on the origin of the screen's desktop-pixel coordinate space - let screenLeftCss = screenLeft.value; - let screenTopCss = screenTop.value; - - // Convert the screen's device pixel dimensions to CSS px dimensions - screen.GetAvailRect(screenLeft, screenTop, screenWidth, screenHeight); - let cssToDevScale = screen.defaultCSSScaleFactor; - let screenRightCss = screenLeftCss + screenWidth.value / cssToDevScale; - let screenBottomCss = screenTopCss + screenHeight.value / cssToDevScale; - - // Pull the window within the screen's bounds. - // First, ensure the left edge is on-screen - if (aLeft < screenLeftCss) { - aLeft = screenLeftCss; - } - // Then check the resulting right edge, and reduce it if necessary. - let right = aLeft + aWidth; - if (right > screenRightCss) { - right = screenRightCss; - // See if we can move the left edge leftwards to maintain width. - if (aLeft > screenLeftCss) { - aLeft = Math.max(right - aWidth, screenLeftCss); - } - } - // Finally, update aWidth to account for the adjusted left and right edges. - aWidth = right - aLeft; - - // Do the same in the vertical dimension. - // First, ensure the top edge is on-screen - if (aTop < screenTopCss) { - aTop = screenTopCss; - } - // Then check the resulting right edge, and reduce it if necessary. - let bottom = aTop + aHeight; - if (bottom > screenBottomCss) { - bottom = screenBottomCss; - // See if we can move the top edge upwards to maintain height. - if (aTop > screenTopCss) { - aTop = Math.max(bottom - aHeight, screenTopCss); - } - } - // Finally, update aHeight to account for the adjusted top and bottom edges. - aHeight = bottom - aTop; - } - - // Only modify those aspects which aren't correct yet - if (!isNaN(aLeft) && !isNaN(aTop) && (aLeft != win_("screenX") || aTop != win_("screenY"))) { - aWindow.moveTo(aLeft, aTop); - } - if (aWidth && aHeight && (aWidth != win_("width") || aHeight != win_("height"))) { - // Don't resize the window if it's currently maximized and we would - // maximize it again shortly after. - if (aSizeMode != "maximized" || win_("sizemode") != "maximized") { - aWindow.resizeTo(aWidth, aHeight); - } - } - - // Restore window state - if (aSizeMode && win_("sizemode") != aSizeMode) - { - switch (aSizeMode) - { - case "maximized": - aWindow.maximize(); - break; - case "minimized": - aWindow.minimize(); - break; - case "normal": - aWindow.restore(); - break; - } - } - var sidebar = aWindow.document.getElementById("sidebar-box"); - if (sidebar.getAttribute("sidebarcommand") != aSidebar) { - aWindow.toggleSidebar(aSidebar); - } - // since resizing/moving a window brings it to the foreground, - // we might want to re-focus the last focused window - if (this.windowToFocus) { - this.windowToFocus.focus(); - } - }, - - /** - * Restores cookies - * @param aCookies - * Array of cookie objects - */ - restoreCookies: function ssi_restoreCookies(aCookies) { - // MAX_EXPIRY should be 2^63-1, but JavaScript can't handle that precision - var MAX_EXPIRY = Math.pow(2, 62); - for (let i = 0; i < aCookies.length; i++) { - var cookie = aCookies[i]; - try { - Services.cookies.add(cookie.host, cookie.path || "", cookie.name || "", - cookie.value, !!cookie.secure, !!cookie.httponly, true, - "expiry" in cookie ? cookie.expiry : MAX_EXPIRY, {}); - } - catch (ex) { Cu.reportError(ex); } // don't let a single cookie stop recovering - } - }, - - /* ........ Disk Access .............. */ - - /** - * save state delayed by N ms - * marks window as dirty (i.e. data update can't be skipped) - * @param aWindow - * Window reference - * @param aDelay - * Milliseconds to delay - */ - saveStateDelayed: function ssi_saveStateDelayed(aWindow, aDelay) { - if (aWindow) { - this._dirtyWindows[aWindow.__SSi] = true; - } - - if (!this._saveTimer) { - // interval until the next disk operation is allowed - var minimalDelay = this._lastSaveTime + this._interval - Date.now(); - - // if we have to wait, set a timer, otherwise saveState directly - aDelay = Math.max(minimalDelay, aDelay || 2000); - if (aDelay > 0) { - this._saveTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - this._saveTimer.init(this, aDelay, Ci.nsITimer.TYPE_ONE_SHOT); - } - else { - this.saveState(); - } - } - }, - - /** - * save state to disk - * @param aUpdateAll - * Bool update all windows - */ - saveState: function ssi_saveState(aUpdateAll) { - // If crash recovery is disabled, we only want to resume with pinned tabs - // if we crash. - let pinnedOnly = this._loadState == STATE_RUNNING && !this._resume_from_crash; - - var oState = this._getCurrentState(aUpdateAll, pinnedOnly); - if (!oState) { - return; - } - - // Forget about private windows. - for (let i = oState.windows.length - 1; i >= 0; i--) { - if (oState.windows[i].isPrivate) { - oState.windows.splice(i, 1); - if (oState.selectedWindow >= i) { - oState.selectedWindow--; - } - } - } - - for (let i = oState._closedWindows.length - 1; i >= 0; i--) { - if (oState._closedWindows[i].isPrivate) { - oState._closedWindows.splice(i, 1); - } - } - -#ifndef XP_MACOSX - // We want to restore closed windows that are marked with _shouldRestore. - // We're doing this here because we want to control this only when saving - // the file. - while (oState._closedWindows.length) { - let i = oState._closedWindows.length - 1; - if (oState._closedWindows[i]._shouldRestore) { - delete oState._closedWindows[i]._shouldRestore; - oState.windows.unshift(oState._closedWindows.pop()); - } - else { - // We only need to go until we hit !needsRestore since we're going in reverse - break; - } - } -#endif - - if (pinnedOnly) { - // Save original resume_session_once preference for when quiting browser, - // otherwise session will be restored next time browser starts and we - // only want it to be restored in the case of a crash. - if (this._resume_session_once_on_shutdown == null) { - this._resume_session_once_on_shutdown = - this._prefBranch.getBoolPref("sessionstore.resume_session_once"); - this._prefBranch.setBoolPref("sessionstore.resume_session_once", true); - // flush the preference file so preference will be saved in case of a crash - Services.prefs.savePrefFile(null); - } - } - - // Persist the last session if we deferred restoring it - if (this._lastSessionState) - oState.lastSessionState = this._lastSessionState; - - // Make sure that we keep the previous session if we started with a single - // private window and no non-private windows have been opened, yet. - if (this._deferredInitialState) { - oState.windows = this._deferredInitialState.windows || []; - } - - this._saveStateObject(oState); - }, - - /** - * write a state object to disk - */ - _saveStateObject: function ssi_saveStateObject(aStateObj) { - let data = this._toJSONString(aStateObj); - - let stateString = this._createSupportsString(data); - Services.obs.notifyObservers(stateString, "sessionstore-state-write", ""); - data = stateString.data; - - // Don't touch the file if an observer has deleted all state data. - if (!data) { - return; - } - - let promise; - // If "sessionstore.resume_from_crash" is true, attempt to backup the - // session file first, before writing to it. - if (this._resume_from_crash) { - // Note that we do not have race conditions here as _SessionFile - // guarantees that any I/O operation is completed before proceeding to - // the next I/O operation. - // Note backup happens only once, on initial save. - promise = this._backupSessionFileOnce; - } else { - promise = Promise.resolve(); - } - - // Attempt to write to the session file (potentially, depending on - // "sessionstore.resume_from_crash" preference, after successful backup). - promise = promise.then(function onSuccess() { - // Write (atomically) to a session file, using a tmp file. - return _SessionFile.write(data); - }); - - // Once the session file is successfully updated, save the time stamp of the - // last save and notify the observers. - promise = promise.then(() => { - this._lastSaveTime = Date.now(); - Services.obs.notifyObservers(null, "sessionstore-state-write-complete", - ""); - }); - }, - - /* ........ Auxiliary Functions .............. */ - - // Wrap a string as a nsISupports - _createSupportsString: function ssi_createSupportsString(aData) { - let string = Cc["@mozilla.org/supports-string;1"] - .createInstance(Ci.nsISupportsString); - string.data = aData; - return string; - }, - - /** - * call a callback for all currently opened browser windows - * (might miss the most recent one) - * @param aFunc - * Callback each window is passed to - */ - _forEachBrowserWindow: function ssi_forEachBrowserWindow(aFunc) { - var windowsEnum = Services.wm.getEnumerator("navigator:browser"); - - while (windowsEnum.hasMoreElements()) { - var window = windowsEnum.getNext(); - if (window.__SSi && !window.closed) { - aFunc.call(this, window); - } - } - }, - - /** - * Returns most recent window - * @returns Window reference - */ - _getMostRecentBrowserWindow: function ssi_getMostRecentBrowserWindow() { - var win = Services.wm.getMostRecentWindow("navigator:browser"); - if (!win) - return null; - if (!win.closed) - return win; - -#ifdef BROKEN_WM_Z_ORDER - win = null; - var windowsEnum = Services.wm.getEnumerator("navigator:browser"); - // this is oldest to newest, so this gets a bit ugly - while (windowsEnum.hasMoreElements()) { - let nextWin = windowsEnum.getNext(); - if (!nextWin.closed) - win = nextWin; - } - return win; -#else - var windowsEnum = - Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", true); - while (windowsEnum.hasMoreElements()) { - win = windowsEnum.getNext(); - if (!win.closed) - return win; - } - return null; -#endif - }, - - /** - * Calls onClose for windows that are determined to be closed but aren't - * destroyed yet, which would otherwise cause getBrowserState and - * setBrowserState to treat them as open windows. - */ - _handleClosedWindows: function ssi_handleClosedWindows() { - var windowsEnum = Services.wm.getEnumerator("navigator:browser"); - - while (windowsEnum.hasMoreElements()) { - var window = windowsEnum.getNext(); - if (window.closed) { - this.onClose(window); - } - } - }, - - /** - * open a new browser window for a given session state - * called when restoring a multi-window session - * @param aState - * Object containing session data - */ - _openWindowWithState: function ssi_openWindowWithState(aState) { - var argString = Cc["@mozilla.org/supports-string;1"]. - createInstance(Ci.nsISupportsString); - argString.data = ""; - - // Build feature string - let features = "chrome,dialog=no,macsuppressanimation,all"; - let winState = aState.windows[0]; - WINDOW_ATTRIBUTES.forEach(function(aFeature) { - // Use !isNaN as an easy way to ignore sizemode and check for numbers - if (aFeature in winState && !isNaN(winState[aFeature])) - features += "," + aFeature + "=" + winState[aFeature]; - }); - - if (winState.isPrivate) { - features += ",private"; - } - - var window = - Services.ww.openWindow(null, this._prefBranch.getCharPref("chromeURL"), - "_blank", features, argString); - - do { - var ID = "window" + Math.random(); - } while (ID in this._statesToRestore); - this._statesToRestore[(window.__SS_restoreID = ID)] = aState; - - return window; - }, - - /** - * Whether or not to resume session, if not recovering from a crash. - * @returns bool - */ - _doResumeSession: function ssi_doResumeSession() { - return this._prefBranch.getIntPref("startup.page") == 3 || - this._prefBranch.getBoolPref("sessionstore.resume_session_once"); - }, - - /** - * whether the user wants to load any other page at startup - * (except the homepage) - needed for determining whether to overwrite the current tabs - * C.f.: nsBrowserContentHandler's defaultArgs implementation. - * @returns bool - */ - _isCmdLineEmpty: function ssi_isCmdLineEmpty(aWindow, aState) { - var pinnedOnly = aState.windows && - aState.windows.every(function (win) - win.tabs.every(function (tab) tab.pinned)); - - let hasFirstArgument = aWindow.arguments && aWindow.arguments[0]; - if (!pinnedOnly) { - let defaultArgs = Cc["@mozilla.org/browser/clh;1"]. - getService(Ci.nsIBrowserHandler).defaultArgs; - if (aWindow.arguments && - aWindow.arguments[0] && - aWindow.arguments[0] == defaultArgs) - hasFirstArgument = false; - } - - return !hasFirstArgument; - }, - - /** - * don't save sensitive data if the user doesn't want to - * (distinguishes between encrypted and non-encrypted sites) - * @param aIsHTTPS - * Bool is encrypted - * @param aUseDefaultPref - * don't do normal check for deferred - * @returns bool - */ - checkPrivacyLevel: function ssi_checkPrivacyLevel(aIsHTTPS, aUseDefaultPref) { - let pref = "sessionstore.privacy_level"; - // If we're in the process of quitting and we're not autoresuming the session - // then we should treat it as a deferred session. We have a different privacy - // pref for that case. - if (!aUseDefaultPref && this._loadState == STATE_QUITTING && !this._doResumeSession()) - pref = "sessionstore.privacy_level_deferred"; - return this._prefBranch.getIntPref(pref) < (aIsHTTPS ? PRIVACY_ENCRYPTED : PRIVACY_FULL); - }, - - /** - * on popup windows, the XULWindow's attributes seem not to be set correctly - * we use thus JSDOMWindow attributes for sizemode and normal window attributes - * (and hope for reasonable values when maximized/minimized - since then - * outerWidth/outerHeight aren't the dimensions of the restored window) - * @param aWindow - * Window reference - * @param aAttribute - * String sizemode | width | height | other window attribute - * @returns string - */ - _getWindowDimension: function ssi_getWindowDimension(aWindow, aAttribute) { - if (aAttribute == "sizemode") { - switch (aWindow.windowState) { - case aWindow.STATE_FULLSCREEN: - case aWindow.STATE_MAXIMIZED: - return "maximized"; - case aWindow.STATE_MINIMIZED: - return "minimized"; - default: - return "normal"; - } - } - - var dimension; - switch (aAttribute) { - case "width": - dimension = aWindow.outerWidth; - break; - case "height": - dimension = aWindow.outerHeight; - break; - default: - dimension = aAttribute in aWindow ? aWindow[aAttribute] : ""; - break; - } - - if (aWindow.windowState == aWindow.STATE_NORMAL) { - return dimension; - } - return aWindow.document.documentElement.getAttribute(aAttribute) || dimension; - }, - - /** - * Get nsIURI from string - * @param string - * @returns nsIURI - */ - _getURIFromString: function ssi_getURIFromString(aString) { - return Services.io.newURI(aString, null, null); - }, - - /** - * @param aState is a session state - * @param aRecentCrashes is the number of consecutive crashes - * @returns whether a restore page will be needed for the session state - */ - _needsRestorePage: function ssi_needsRestorePage(aState, aRecentCrashes) { - const SIX_HOURS_IN_MS = 6 * 60 * 60 * 1000; - - // don't display the page when there's nothing to restore - let winData = aState.windows || null; - if (!winData || winData.length == 0) - return false; - - // don't wrap a single about:sessionrestore page - if (winData.length == 1 && winData[0].tabs && - winData[0].tabs.length == 1 && winData[0].tabs[0].entries && - winData[0].tabs[0].entries.length == 1 && - winData[0].tabs[0].entries[0].url == "about:sessionrestore") - return false; - - // don't automatically restore in Safe Mode - if (Services.appinfo.inSafeMode) - return true; - - let max_resumed_crashes = - this._prefBranch.getIntPref("sessionstore.max_resumed_crashes"); - let sessionAge = aState.session && aState.session.lastUpdate && - (Date.now() - aState.session.lastUpdate); - - return max_resumed_crashes != -1 && - (aRecentCrashes > max_resumed_crashes || - sessionAge && sessionAge >= SIX_HOURS_IN_MS); - }, - - /** - * Determine if the tab state we're passed is something we should save. This - * is used when closing a tab or closing a window with a single tab - * - * @param aTabState - * The current tab state - * @returns boolean - */ - _shouldSaveTabState: function ssi_shouldSaveTabState(aTabState) { - // If the tab has only a transient about: history entry, no other - // session history, and no userTypedValue, then we don't actually want to - // store this tab's data. - return aTabState.entries.length && - !(aTabState.entries.length == 1 && - (aTabState.entries[0].url == "about:blank" || - aTabState.entries[0].url == "about:newtab") && - !aTabState.userTypedValue); - }, - - /** - * Determine if we can restore history into this tab. - * This will be false when a tab has been removed (usually between - * restoreHistoryPrecursor && restoreHistory) or if the tab is still marked - * as loading. - * - * @param aTab - * @returns boolean - */ - _canRestoreTabHistory: function ssi_canRestoreTabHistory(aTab) { - return aTab.parentNode && aTab.linkedBrowser && - aTab.linkedBrowser.__SS_tabStillLoading; - }, - - /** - * This is going to take a state as provided at startup (via - * nsISessionStartup.state) and split it into 2 parts. The first part - * (defaultState) will be a state that should still be restored at startup, - * while the second part (state) is a state that should be saved for later. - * defaultState will be comprised of windows with only pinned tabs, extracted - * from state. It will contain the cookies that go along with the history - * entries in those tabs. It will also contain window position information. - * - * defaultState will be restored at startup. state will be placed into - * this._lastSessionState and will be kept in case the user explicitly wants - * to restore the previous session (publicly exposed as restoreLastSession). - * - * @param state - * The state, presumably from nsISessionStartup.state - * @returns [defaultState, state] - */ - _prepDataForDeferredRestore: function ssi_prepDataForDeferredRestore(state) { - // Make sure that we don't modify the global state as provided by - // nsSessionStartup.state. Converting the object to a JSON string and - // parsing it again is the easiest way to do that, although not the most - // efficient one. Deferred sessions that don't have automatic session - // restore enabled tend to be a lot smaller though so that this shouldn't - // be a big perf hit. - state = JSON.parse(JSON.stringify(state)); - - let defaultState = { windows: [], selectedWindow: 1 }; - - state.selectedWindow = state.selectedWindow || 1; - - // Look at each window, remove pinned tabs, adjust selectedindex, - // remove window if necessary. - for (let wIndex = 0; wIndex < state.windows.length;) { - let window = state.windows[wIndex]; - window.selected = window.selected || 1; - // We're going to put the state of the window into this object - let pinnedWindowState = { tabs: [], cookies: []}; - for (let tIndex = 0; tIndex < window.tabs.length;) { - if (window.tabs[tIndex].pinned) { - // Adjust window.selected - if (tIndex + 1 < window.selected) - window.selected -= 1; - else if (tIndex + 1 == window.selected) - pinnedWindowState.selected = pinnedWindowState.tabs.length + 2; - // + 2 because the tab isn't actually in the array yet - - // Now add the pinned tab to our window - pinnedWindowState.tabs = - pinnedWindowState.tabs.concat(window.tabs.splice(tIndex, 1)); - // We don't want to increment tIndex here. - continue; - } - tIndex++; - } - - // At this point the window in the state object has been modified (or not) - // We want to build the rest of this new window object if we have pinnedTabs. - if (pinnedWindowState.tabs.length) { - // First get the other attributes off the window - WINDOW_ATTRIBUTES.forEach(function(attr) { - if (attr in window) { - pinnedWindowState[attr] = window[attr]; - delete window[attr]; - } - }); - // We're just copying position data into the pinned window. - // Not copying over: - // - _closedTabs - // - extData - // - isPopup - // - hidden - - // Assign a unique ID to correlate the window to be opened with the - // remaining data - window.__lastSessionWindowID = pinnedWindowState.__lastSessionWindowID - = "" + Date.now() + Math.random(); - - // Extract the cookies that belong with each pinned tab - this._splitCookiesFromWindow(window, pinnedWindowState); - - // Actually add this window to our defaultState - defaultState.windows.push(pinnedWindowState); - // Remove the window from the state if it doesn't have any tabs - if (!window.tabs.length) { - if (wIndex + 1 <= state.selectedWindow) - state.selectedWindow -= 1; - else if (wIndex + 1 == state.selectedWindow) - defaultState.selectedIndex = defaultState.windows.length + 1; - - state.windows.splice(wIndex, 1); - // We don't want to increment wIndex here. - continue; - } - - - } - wIndex++; - } - - return [defaultState, state]; - }, - - /** - * Splits out the cookies from aWinState into aTargetWinState based on the - * tabs that are in aTargetWinState. - * This alters the state of aWinState and aTargetWinState. - */ - _splitCookiesFromWindow: - function ssi_splitCookiesFromWindow(aWinState, aTargetWinState) { - if (!aWinState.cookies || !aWinState.cookies.length) - return; - - // Get the hosts for history entries in aTargetWinState - let cookieHosts = {}; - aTargetWinState.tabs.forEach(function(tab) { - tab.entries.forEach(function(entry) { - this._extractHostsForCookiesFromEntry(entry, cookieHosts, false); - }, this); - }, this); - - // By creating a regex we reduce overhead and there is only one loop pass - // through either array (cookieHosts and aWinState.cookies). - let hosts = Object.keys(cookieHosts).join("|").replace("\\.", "\\.", "g"); - // If we don't actually have any hosts, then we don't want to do anything. - if (!hosts.length) - return; - let cookieRegex = new RegExp(".*(" + hosts + ")"); - for (let cIndex = 0; cIndex < aWinState.cookies.length;) { - if (cookieRegex.test(aWinState.cookies[cIndex].host)) { - aTargetWinState.cookies = - aTargetWinState.cookies.concat(aWinState.cookies.splice(cIndex, 1)); - continue; - } - cIndex++; - } - }, - - /** - * Converts a JavaScript object into a JSON string - * (see http://www.json.org/ for more information). - * - * The inverse operation consists of JSON.parse(JSON_string). - * - * @param aJSObject is the object to be converted - * @returns the object's JSON representation - */ - _toJSONString: function ssi_toJSONString(aJSObject) { - return JSON.stringify(aJSObject); - }, - - _sendRestoreCompletedNotifications: function ssi_sendRestoreCompletedNotifications() { - // not all windows restored, yet - if (this._restoreCount > 1) { - this._restoreCount--; - return; - } - - // observers were already notified - if (this._restoreCount == -1) - return; - - // This was the last window restored at startup, notify observers. - Services.obs.notifyObservers(null, - this._browserSetState ? NOTIFY_BROWSER_STATE_RESTORED : NOTIFY_WINDOWS_RESTORED, - ""); - - this._browserSetState = false; - this._restoreCount = -1; - }, - - /** - * Set the given window's busy state - * @param aWindow the window - * @param aValue the window's busy state - */ - _setWindowStateBusyValue: - function ssi_changeWindowStateBusyValue(aWindow, aValue) { - - this._windows[aWindow.__SSi].busy = aValue; - - // Keep the to-be-restored state in sync because that is returned by - // getWindowState() as long as the window isn't loaded, yet. - if (!this._isWindowLoaded(aWindow)) { - let stateToRestore = this._statesToRestore[aWindow.__SS_restoreID].windows[0]; - stateToRestore.busy = aValue; - } - }, - - /** - * Set the given window's state to 'not busy'. - * @param aWindow the window - */ - _setWindowStateReady: function ssi_setWindowStateReady(aWindow) { - this._setWindowStateBusyValue(aWindow, false); - this._sendWindowStateEvent(aWindow, "Ready"); - }, - - /** - * Set the given window's state to 'busy'. - * @param aWindow the window - */ - _setWindowStateBusy: function ssi_setWindowStateBusy(aWindow) { - this._setWindowStateBusyValue(aWindow, true); - this._sendWindowStateEvent(aWindow, "Busy"); - }, - - /** - * Dispatch an SSWindowState_____ event for the given window. - * @param aWindow the window - * @param aType the type of event, SSWindowState will be prepended to this string - */ - _sendWindowStateEvent: function ssi_sendWindowStateEvent(aWindow, aType) { - let event = aWindow.document.createEvent("Events"); - event.initEvent("SSWindowState" + aType, true, false); - aWindow.dispatchEvent(event); - }, - - /** - * Dispatch the SSTabRestored event for the given tab. - * @param aTab the which has been restored - */ - _sendTabRestoredNotification: function ssi_sendTabRestoredNotification(aTab) { - let event = aTab.ownerDocument.createEvent("Events"); - event.initEvent("SSTabRestored", true, false); - aTab.dispatchEvent(event); - }, - - /** - * @param aWindow - * Window reference - * @returns whether this window's data is still cached in _statesToRestore - * because it's not fully loaded yet - */ - _isWindowLoaded: function ssi_isWindowLoaded(aWindow) { - return !aWindow.__SS_restoreID; - }, - - /** - * Replace "Loading..." with the tab label (with minimal side-effects) - * @param aString is the string the title is stored in - * @param aTabbrowser is a tabbrowser object, containing aTab - * @param aTab is the tab whose title we're updating & using - * - * @returns aString that has been updated with the new title - */ - _replaceLoadingTitle : function ssi_replaceLoadingTitle(aString, aTabbrowser, aTab) { - if (aString == aTabbrowser.mStringBundle.getString("tabs.connecting")) { - aTabbrowser.setTabTitle(aTab); - [aString, aTab.label] = [aTab.label, aString]; - } - return aString; - }, - - /** - * Resize this._closedWindows to the value of the pref, except in the case - * where we don't have any non-popup windows on Windows and Linux. Then we must - * resize such that we have at least one non-popup window. - */ - _capClosedWindows : function ssi_capClosedWindows() { - if (this._closedWindows.length <= this._max_windows_undo) - return; - let spliceTo = this._max_windows_undo; -#ifndef XP_MACOSX - let normalWindowIndex = 0; - // try to find a non-popup window in this._closedWindows - while (normalWindowIndex < this._closedWindows.length && - !!this._closedWindows[normalWindowIndex].isPopup) - normalWindowIndex++; - if (normalWindowIndex >= this._max_windows_undo) - spliceTo = normalWindowIndex + 1; -#endif - this._closedWindows.splice(spliceTo, this._closedWindows.length); - }, - - _clearRestoringWindows: function ssi_clearRestoringWindows() { - for (let i = 0; i < this._closedWindows.length; i++) { - delete this._closedWindows[i]._shouldRestore; - } - }, - - /** - * Reset state to prepare for a new session state to be restored. - */ - _resetRestoringState: function ssi_initRestoringState() { - TabRestoreQueue.reset(); - this._tabsRestoringCount = 0; - }, - - /** - * Reset the restoring state for a particular tab. This will be called when - * removing a tab or when a tab needs to be reset (it's being overwritten). - * - * @param aTab - * The tab that will be "reset" - */ - _resetTabRestoringState: function ssi_resetTabRestoringState(aTab) { - let window = aTab.ownerDocument.defaultView; - let browser = aTab.linkedBrowser; - - // Keep the tab's previous state for later in this method - let previousState = browser.__SS_restoreState; - - // The browser is no longer in any sort of restoring state. - delete browser.__SS_restoreState; - - aTab.removeAttribute("pending"); - browser.removeAttribute("pending"); - - // We want to decrement window.__SS_tabsToRestore here so that we always - // decrement it AFTER a tab is done restoring or when a tab gets "reset". - window.__SS_tabsToRestore--; - - // Remove the progress listener if we should. - this._removeTabsProgressListener(window); - - if (previousState == TAB_STATE_RESTORING) { - if (this._tabsRestoringCount) - this._tabsRestoringCount--; - } - else if (previousState == TAB_STATE_NEEDS_RESTORE) { - // Make sure the session history listener is removed. This is normally - // done in restoreTab, but this tab is being removed before that gets called. - this._removeSHistoryListener(aTab); - - // Make sure that the tab is removed from the list of tabs to restore. - // Again, this is normally done in restoreTab, but that isn't being called - // for this tab. - TabRestoreQueue.remove(aTab); - } - }, - - /** - * Add the tabs progress listener to the window if it isn't already - * - * @param aWindow - * The window to add our progress listener to - */ - _ensureTabsProgressListener: function ssi_ensureTabsProgressListener(aWindow) { - let tabbrowser = aWindow.gBrowser; - if (tabbrowser.mTabsProgressListeners.indexOf(gRestoreTabsProgressListener) == -1) - tabbrowser.addTabsProgressListener(gRestoreTabsProgressListener); - }, - - /** - * Attempt to remove the tabs progress listener from the window. - * - * @param aWindow - * The window from which to remove our progress listener from - */ - _removeTabsProgressListener: function ssi_removeTabsProgressListener(aWindow) { - // If there are no tabs left to restore (or restoring) in this window, then - // we can safely remove the progress listener from this window. - if (!aWindow.__SS_tabsToRestore) - aWindow.gBrowser.removeTabsProgressListener(gRestoreTabsProgressListener); - }, - - /** - * Remove the session history listener from the tab's browser if there is one. - * - * @param aTab - * The tab who's browser to remove the listener - */ - _removeSHistoryListener: function ssi_removeSHistoryListener(aTab) { - let browser = aTab.linkedBrowser; - if (browser.__SS_shistoryListener) { - browser.webNavigation.sessionHistory. - removeSHistoryListener(browser.__SS_shistoryListener); - delete browser.__SS_shistoryListener; - } - } -}; - -/** - * Priority queue that keeps track of a list of tabs to restore and returns - * the tab we should restore next, based on priority rules. We decide between - * pinned, visible and hidden tabs in that and FIFO order. Hidden tabs are only - * restored with restore_hidden_tabs=true. - */ -var TabRestoreQueue = { - // The separate buckets used to store tabs. - tabs: {priority: [], visible: [], hidden: []}, - - // Preferences used by the TabRestoreQueue to determine which tabs - // are restored automatically and which tabs will be on-demand. - prefs: { - // Lazy getter that returns whether tabs are restored on demand. - get restoreOnDemand() { - let updateValue = () => { - let value = Services.prefs.getBoolPref(PREF); - let definition = {value: value, configurable: true}; - Object.defineProperty(this, "restoreOnDemand", definition); - return value; - } - - const PREF = "browser.sessionstore.restore_on_demand"; - Services.prefs.addObserver(PREF, updateValue, false); - return updateValue(); - }, - - // Lazy getter that returns whether pinned tabs are restored on demand. - get restorePinnedTabsOnDemand() { - let updateValue = () => { - let value = Services.prefs.getBoolPref(PREF); - let definition = {value: value, configurable: true}; - Object.defineProperty(this, "restorePinnedTabsOnDemand", definition); - return value; - } - - const PREF = "browser.sessionstore.restore_pinned_tabs_on_demand"; - Services.prefs.addObserver(PREF, updateValue, false); - return updateValue(); - }, - - // Lazy getter that returns whether we should restore hidden tabs. - get restoreHiddenTabs() { - let updateValue = () => { - let value = Services.prefs.getBoolPref(PREF); - let definition = {value: value, configurable: true}; - Object.defineProperty(this, "restoreHiddenTabs", definition); - return value; - } - - const PREF = "browser.sessionstore.restore_hidden_tabs"; - Services.prefs.addObserver(PREF, updateValue, false); - return updateValue(); - } - }, - - // Resets the queue and removes all tabs. - reset: function () { - this.tabs = {priority: [], visible: [], hidden: []}; - }, - - // Adds a tab to the queue and determines its priority bucket. - add: function (tab) { - let {priority, hidden, visible} = this.tabs; - - if (tab.pinned) { - priority.push(tab); - } else if (tab.hidden) { - hidden.push(tab); - } else { - visible.push(tab); - } - }, - - // Removes a given tab from the queue, if it's in there. - remove: function (tab) { - let {priority, hidden, visible} = this.tabs; - - // We'll always check priority first since we don't - // have an indicator if a tab will be there or not. - let set = priority; - let index = set.indexOf(tab); - - if (index == -1) { - set = tab.hidden ? hidden : visible; - index = set.indexOf(tab); - } - - if (index > -1) { - set.splice(index, 1); - } - }, - - // Returns and removes the tab with the highest priority. - shift: function () { - let set; - let {priority, hidden, visible} = this.tabs; - - let {restoreOnDemand, restorePinnedTabsOnDemand} = this.prefs; - let restorePinned = !(restoreOnDemand && restorePinnedTabsOnDemand); - if (restorePinned && priority.length) { - set = priority; - } else if (!restoreOnDemand) { - if (visible.length) { - set = visible; - } else if (this.prefs.restoreHiddenTabs && hidden.length) { - set = hidden; - } - } - - return set && set.shift(); - }, - - // Moves a given tab from the 'hidden' to the 'visible' bucket. - hiddenToVisible: function (tab) { - let {hidden, visible} = this.tabs; - let index = hidden.indexOf(tab); - - if (index > -1) { - hidden.splice(index, 1); - visible.push(tab); - } else { - throw new Error("restore queue: hidden tab not found"); - } - }, - - // Moves a given tab from the 'visible' to the 'hidden' bucket. - visibleToHidden: function (tab) { - let {visible, hidden} = this.tabs; - let index = visible.indexOf(tab); - - if (index > -1) { - visible.splice(index, 1); - hidden.push(tab); - } else { - throw new Error("restore queue: visible tab not found"); - } - } -}; - -// A map storing a closed window's state data until it goes aways (is GC'ed). -// This ensures that API clients can still read (but not write) states of -// windows they still hold a reference to but we don't. -var DyingWindowCache = { - _data: new WeakMap(), - - has: function (window) { - return this._data.has(window); - }, - - get: function (window) { - return this._data.get(window); - }, - - set: function (window, data) { - this._data.set(window, data); - }, - - remove: function (window) { - this._data.delete(window); - } -}; - -// A set of tab attributes to persist. We will read a given list of tab -// attributes when collecting tab data and will re-set those attributes when -// the given tab data is restored to a new tab. -var TabAttributes = { - _attrs: new Set(), - - // We never want to directly read or write those attributes. - // 'image' should not be accessed directly but handled by using the - // gBrowser.getIcon()/setIcon() methods. - // 'pending' is used internal by sessionstore and managed accordingly. - // 'skipbackgroundnotify' is used internal by tabbrowser.xml. - _skipAttrs: new Set(["image", "pending", "skipbackgroundnotify"]), - - persist: function (name) { - if (this._attrs.has(name) || this._skipAttrs.has(name)) { - return false; - } - - this._attrs.add(name); - return true; - }, - - get: function (tab) { - let data = {}; - - for (let name of this._attrs) { - if (tab.hasAttribute(name)) { - data[name] = tab.getAttribute(name); - } - } - - return data; - }, - - set: function (tab, data = {}) { - // Clear attributes. - for (let name of this._attrs) { - tab.removeAttribute(name); - } - - // Set attributes. - for (let name in data) { - tab.setAttribute(name, data[name]); - } - } -}; - -// This is used to help meter the number of restoring tabs. This is the control -// point for telling the next tab to restore. It gets attached to each gBrowser -// via gBrowser.addTabsProgressListener -var gRestoreTabsProgressListener = { - onStateChange: function(aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) { - // Ignore state changes on browsers that we've already restored and state - // changes that aren't applicable. - if (aBrowser.__SS_restoreState && - aBrowser.__SS_restoreState == TAB_STATE_RESTORING && - aStateFlags & Ci.nsIWebProgressListener.STATE_STOP && - aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK && - aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) { - // We need to reset the tab before starting the next restore. - let win = aBrowser.ownerDocument.defaultView; - let tab = win.gBrowser.getTabForBrowser(aBrowser); - SessionStoreInternal._resetTabRestoringState(tab); - SessionStoreInternal.restoreNextTab(); - } - } -}; - -// A SessionStoreSHistoryListener will be attached to each browser before it is -// restored. We need to catch reloads that occur before the tab is restored -// because otherwise, docShell will reload an old URI (usually about:blank). -function SessionStoreSHistoryListener(aTab) { - this.tab = aTab; -} -SessionStoreSHistoryListener.prototype = { - QueryInterface: XPCOMUtils.generateQI([ - Ci.nsISHistoryListener, - Ci.nsISupportsWeakReference - ]), - browser: null, - OnHistoryNewEntry: function(aNewURI) { }, - OnHistoryGoBack: function(aBackURI) { return true; }, - OnHistoryGoForward: function(aForwardURI) { return true; }, - OnHistoryGotoIndex: function(aIndex, aGotoURI) { return true; }, - OnHistoryPurge: function(aNumEntries) { return true; }, - OnHistoryReload: function(aReloadURI, aReloadFlags) { - // On reload, we want to make sure that session history loads the right - // URI. In order to do that, we will juet call restoreTab. That will remove - // the history listener and load the right URI. - SessionStoreInternal.restoreTab(this.tab); - // Returning false will stop the load that docshell is attempting. - return false; - } -} - -// See toolkit/forgetaboutsite/ForgetAboutSite.jsm -String.prototype.hasRootDomain = function hasRootDomain(aDomain) { - let index = this.indexOf(aDomain); - if (index == -1) - return false; - - if (this == aDomain) - return true; - - let prevChar = this[index - 1]; - return (index == (this.length - aDomain.length)) && - (prevChar == "." || prevChar == "/"); -} diff --git a/components/sessionstore/XPathGenerator.jsm b/components/sessionstore/XPathGenerator.jsm deleted file mode 100644 index 83ff2b8..0000000 --- a/components/sessionstore/XPathGenerator.jsm +++ /dev/null @@ -1,97 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -this.EXPORTED_SYMBOLS = ["XPathGenerator"]; - -this.XPathGenerator = { - // these two hashes should be kept in sync - namespaceURIs: { "xhtml": "http://www.w3.org/1999/xhtml" }, - namespacePrefixes: { "http://www.w3.org/1999/xhtml": "xhtml" }, - - /** - * Generates an approximate XPath query to an (X)HTML node - */ - generate: function sss_xph_generate(aNode) { - // have we reached the document node already? - if (!aNode.parentNode) - return ""; - - // Access localName, namespaceURI just once per node since it's expensive. - let nNamespaceURI = aNode.namespaceURI; - let nLocalName = aNode.localName; - - let prefix = this.namespacePrefixes[nNamespaceURI] || null; - let tag = (prefix ? prefix + ":" : "") + this.escapeName(nLocalName); - - // stop once we've found a tag with an ID - if (aNode.id) - return "//" + tag + "[@id=" + this.quoteArgument(aNode.id) + "]"; - - // count the number of previous sibling nodes of the same tag - // (and possible also the same name) - let count = 0; - let nName = aNode.name || null; - for (let n = aNode; (n = n.previousSibling); ) - if (n.localName == nLocalName && n.namespaceURI == nNamespaceURI && - (!nName || n.name == nName)) - count++; - - // recurse until hitting either the document node or an ID'd node - return this.generate(aNode.parentNode) + "/" + tag + - (nName ? "[@name=" + this.quoteArgument(nName) + "]" : "") + - (count ? "[" + (count + 1) + "]" : ""); - }, - - /** - * Resolves an XPath query generated by XPathGenerator.generate - */ - resolve: function sss_xph_resolve(aDocument, aQuery) { - let xptype = Components.interfaces.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE; - return aDocument.evaluate(aQuery, aDocument, this.resolveNS, xptype, null).singleNodeValue; - }, - - /** - * Namespace resolver for the above XPath resolver - */ - resolveNS: function sss_xph_resolveNS(aPrefix) { - return XPathGenerator.namespaceURIs[aPrefix] || null; - }, - - /** - * @returns valid XPath for the given node (usually just the local name itself) - */ - escapeName: function sss_xph_escapeName(aName) { - // we can't just use the node's local name, if it contains - // special characters (cf. bug 485482) - return /^\w+$/.test(aName) ? aName : - "*[local-name()=" + this.quoteArgument(aName) + "]"; - }, - - /** - * @returns a properly quoted string to insert into an XPath query - */ - quoteArgument: function sss_xph_quoteArgument(aArg) { - return !/'/.test(aArg) ? "'" + aArg + "'" : - !/"/.test(aArg) ? '"' + aArg + '"' : - "concat('" + aArg.replace(/'+/g, "',\"$&\",'") + "')"; - }, - - /** - * @returns an XPath query to all savable form field nodes - */ - get restorableFormNodes() { - // for a comprehensive list of all available <INPUT> types see - // http://mxr.mozilla.org/mozilla-central/search?string=kInputTypeTable - let ignoreTypes = ["password", "hidden", "button", "image", "submit", "reset"]; - // XXXzeniko work-around until lower-case has been implemented (bug 398389) - let toLowerCase = '"ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"'; - let ignore = "not(translate(@type, " + toLowerCase + ")='" + - ignoreTypes.join("' or translate(@type, " + toLowerCase + ")='") + "')"; - let formNodesXPath = "//textarea|//select|//xhtml:textarea|//xhtml:select|" + - "//input[" + ignore + "]|//xhtml:input[" + ignore + "]"; - - delete this.restorableFormNodes; - return (this.restorableFormNodes = formNodesXPath); - } -}; diff --git a/components/sessionstore/_SessionFile.jsm b/components/sessionstore/_SessionFile.jsm deleted file mode 100644 index 62b4d16..0000000 --- a/components/sessionstore/_SessionFile.jsm +++ /dev/null @@ -1,314 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -this.EXPORTED_SYMBOLS = ["_SessionFile"]; - -/** - * Implementation of all the disk I/O required by the session store. - * This is a private API, meant to be used only by the session store. - * It will change. Do not use it for any other purpose. - * - * Note that this module implicitly depends on one of two things: - * 1. either the asynchronous file I/O system enqueues its requests - * and never attempts to simultaneously execute two I/O requests on - * the files used by this module from two distinct threads; or - * 2. the clients of this API are well-behaved and do not place - * concurrent requests to the files used by this module. - * - * Otherwise, we could encounter bugs, especially under Windows, - * e.g. if a request attempts to write sessionstore.js while - * another attempts to copy that file. - * - * This implementation uses OS.File, which guarantees property 1. - */ - -const Cu = Components.utils; -const Cc = Components.classes; -const Ci = Components.interfaces; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/osfile.jsm"); -Cu.import("resource://gre/modules/Promise.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", - "resource://gre/modules/NetUtil.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", - "resource://gre/modules/FileUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Task", - "resource://gre/modules/Task.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "console", - "resource://gre/modules/Console.jsm"); - -// An encoder to UTF-8. -XPCOMUtils.defineLazyGetter(this, "gEncoder", function () { - return new TextEncoder(); -}); -// A decoder. -XPCOMUtils.defineLazyGetter(this, "gDecoder", function () { - return new TextDecoder(); -}); - -this._SessionFile = { - /** - * A promise fulfilled once initialization (either synchronous or - * asynchronous) is complete. - */ - promiseInitialized: function SessionFile_initialized() { - return SessionFileInternal.promiseInitialized; - }, - /** - * Read the contents of the session file, asynchronously. - */ - read: function SessionFile_read() { - return SessionFileInternal.read(); - }, - /** - * Read the contents of the session file, synchronously. - */ - syncRead: function SessionFile_syncRead() { - return SessionFileInternal.syncRead(); - }, - /** - * Write the contents of the session file, asynchronously. - */ - write: function SessionFile_write(aData) { - return SessionFileInternal.write(aData); - }, - /** - * Create a backup copy, asynchronously. - */ - createBackupCopy: function SessionFile_createBackupCopy() { - return SessionFileInternal.createBackupCopy(); - }, - /** - * Wipe the contents of the session file, asynchronously. - */ - wipe: function SessionFile_wipe() { - return SessionFileInternal.wipe(); - } -}; - -Object.freeze(_SessionFile); - -/** - * Utilities for dealing with promises and Task.jsm - */ -const TaskUtils = { - /** - * Add logging to a promise. - * - * @param {Promise} promise - * @return {Promise} A promise behaving as |promise|, but with additional - * logging in case of uncaught error. - */ - captureErrors: function captureErrors(promise) { - return promise.then( - null, - function onError(reason) { - console.error("Uncaught asynchronous error:", reason); - throw reason; - } - ); - }, - /** - * Spawn a new Task from a generator. - * - * This function behaves as |Task.spawn|, with the exception that it - * adds logging in case of uncaught error. For more information, see - * the documentation of |Task.jsm|. - * - * @param {generator} gen Some generator. - * @return {Promise} A promise built from |gen|, with the same semantics - * as |Task.spawn(gen)|. - */ - spawn: function spawn(gen) { - return this.captureErrors(Task.spawn(gen)); - } -}; - -var SessionFileInternal = { - /** - * A promise fulfilled once initialization is complete - */ - promiseInitialized: Promise.defer(), - - /** - * The path to sessionstore.js - */ - path: OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.js"), - - /** - * The path to sessionstore.bak - */ - backupPath: OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.bak"), - - /** - * Utility function to safely read a file synchronously. - * @param aPath - * A path to read the file from. - * @returns string if successful, undefined otherwise. - */ - readAuxSync: function ssfi_readAuxSync(aPath) { - let text; - try { - let file = new FileUtils.File(aPath); - let chan = NetUtil.newChannel({ - uri: NetUtil.newURI(file), - loadUsingSystemPrincipal: true - }); - let stream = chan.open(); - text = NetUtil.readInputStreamToString(stream, stream.available(), - {charset: "utf-8"}); - } catch (e if e.result == Components.results.NS_ERROR_FILE_NOT_FOUND) { - // Ignore exceptions about non-existent files. - } catch (ex) { - // Any other error. - console.error("Uncaught error:", ex); - } finally { - return text; - } - }, - - /** - * Read the sessionstore file synchronously. - * - * This function is meant to serve as a fallback in case of race - * between a synchronous usage of the API and asynchronous - * initialization. - * - * In case if sessionstore.js file does not exist or is corrupted (something - * happened between backup and write), attempt to read the sessionstore.bak - * instead. - */ - syncRead: function ssfi_syncRead() { - // First read the sessionstore.js. - let text = this.readAuxSync(this.path); - if (typeof text === "undefined") { - // If sessionstore.js does not exist or is corrupted, read sessionstore.bak. - text = this.readAuxSync(this.backupPath); - } - return text || ""; - }, - - /** - * Utility function to safely read a file asynchronously. - * @param aPath - * A path to read the file from. - * @param aReadOptions - * Read operation options. - * |outExecutionDuration| option will be reused and can be - * incrementally updated by the worker process. - * @returns string if successful, undefined otherwise. - */ - readAux: function ssfi_readAux(aPath, aReadOptions) { - let self = this; - return TaskUtils.spawn(function () { - let text; - try { - let bytes = yield OS.File.read(aPath, undefined, aReadOptions); - text = gDecoder.decode(bytes); - } catch (ex if self._isNoSuchFile(ex)) { - // Ignore exceptions about non-existent files. - } catch (ex) { - // Any other error. - console.error("Uncaught error - with the file: " + self.path, ex); - } - throw new Task.Result(text); - }); - }, - - /** - * Read the sessionstore file asynchronously. - * - * In case sessionstore.js file does not exist or is corrupted (something - * happened between backup and write), attempt to read the sessionstore.bak - * instead. - */ - read: function ssfi_read() { - let self = this; - return TaskUtils.spawn(function task() { - // Specify |outExecutionDuration| option to hold the combined duration of - // the asynchronous reads off the main thread (of both sessionstore.js and - // sessionstore.bak, if necessary). If sessionstore.js does not exist or - // is corrupted, |outExecutionDuration| will register the time it took to - // attempt to read the file. It will then be subsequently incremented by - // the read time of sessionsore.bak. - let readOptions = { - outExecutionDuration: null - }; - // First read the sessionstore.js. - let text = yield self.readAux(self.path, readOptions); - if (typeof text === "undefined") { - // If sessionstore.js does not exist or is corrupted, read the - // sessionstore.bak. - text = yield self.readAux(self.backupPath, readOptions); - } - // Return either the content of the sessionstore.bak if it was read - // successfully or an empty string otherwise. - throw new Task.Result(text || ""); - }); - }, - - write: function ssfi_write(aData) { - let refObj = {}; - let self = this; - return TaskUtils.spawn(function task() { - let bytes = gEncoder.encode(aData); - - try { - let promise = OS.File.writeAtomic(self.path, bytes, {tmpPath: self.path + ".tmp"}); - yield promise; - } catch (ex) { - console.error("Could not write session state file: " + self.path, ex); - } - }); - }, - - createBackupCopy: function ssfi_createBackupCopy() { - let backupCopyOptions = { - outExecutionDuration: null - }; - let self = this; - return TaskUtils.spawn(function task() { - try { - yield OS.File.move(self.path, self.backupPath, backupCopyOptions); - } catch (ex if self._isNoSuchFile(ex)) { - // Ignore exceptions about non-existent files. - } catch (ex) { - console.error("Could not backup session state file: " + self.path, ex); - throw ex; - } - }); - }, - - wipe: function ssfi_wipe() { - let self = this; - return TaskUtils.spawn(function task() { - try { - yield OS.File.remove(self.path); - } catch (ex if self._isNoSuchFile(ex)) { - // Ignore exceptions about non-existent files. - } catch (ex) { - console.error("Could not remove session state file: " + self.path, ex); - throw ex; - } - - try { - yield OS.File.remove(self.backupPath); - } catch (ex if self._isNoSuchFile(ex)) { - // Ignore exceptions about non-existent files. - } catch (ex) { - console.error("Could not remove session state backup file: " + self.path, ex); - throw ex; - } - }); - }, - - _isNoSuchFile: function ssfi_isNoSuchFile(aReason) { - return aReason instanceof OS.File.Error && aReason.becauseNoSuchFile; - } -}; diff --git a/components/sessionstore/content/aboutSessionRestore.js b/components/sessionstore/content/aboutSessionRestore.js deleted file mode 100644 index 2b6f9ea..0000000 --- a/components/sessionstore/content/aboutSessionRestore.js +++ /dev/null @@ -1,320 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var Cc = Components.classes; -var Ci = Components.interfaces; -var Cu = Components.utils; - -var gStateObject; -var gTreeData; - -// Page initialization - -window.onload = function() { - // the crashed session state is kept inside a textbox so that SessionStore picks it up - // (for when the tab is closed or the session crashes right again) - var sessionData = document.getElementById("sessionData"); - if (!sessionData.value) { - document.getElementById("errorTryAgain").disabled = true; - return; - } - - // remove unneeded braces (added for compatibility with Firefox 2.0 and 3.0) - if (sessionData.value.charAt(0) == '(') - sessionData.value = sessionData.value.slice(1, -1); - try { - gStateObject = JSON.parse(sessionData.value); - } - catch (exJSON) { - var s = new Cu.Sandbox("about:blank", {sandboxName: 'aboutSessionRestore'}); - gStateObject = Cu.evalInSandbox("(" + sessionData.value + ")", s); - // If we couldn't parse the string with JSON.parse originally, make sure - // that the value in the textbox will be parsable. - sessionData.value = JSON.stringify(gStateObject); - } - - // make sure the data is tracked to be restored in case of a subsequent crash - var event = document.createEvent("UIEvents"); - event.initUIEvent("input", true, true, window, 0); - sessionData.dispatchEvent(event); - - initTreeView(); - - document.getElementById("errorTryAgain").focus(); -}; - -function initTreeView() { - var tabList = document.getElementById("tabList"); - var winLabel = tabList.getAttribute("_window_label"); - - gTreeData = []; - gStateObject.windows.forEach(function(aWinData, aIx) { - var winState = { - label: winLabel.replace("%S", (aIx + 1)), - open: true, - checked: true, - ix: aIx - }; - winState.tabs = aWinData.tabs.map(function(aTabData) { - var entry = aTabData.entries[aTabData.index - 1] || { url: "about:blank" }; - var iconURL = aTabData.attributes && aTabData.attributes.image || null; - // don't initiate a connection just to fetch a favicon (see bug 462863) - if (/^https?:/.test(iconURL)) - iconURL = "moz-anno:favicon:" + iconURL; - return { - label: entry.title || entry.url, - checked: true, - src: iconURL, - parent: winState - }; - }); - gTreeData.push(winState); - for (let tab of winState.tabs) - gTreeData.push(tab); - }, this); - - tabList.view = treeView; - tabList.view.selection.select(0); -} - -// User actions - -function restoreSession() { - document.getElementById("errorTryAgain").disabled = true; - - // remove all unselected tabs from the state before restoring it - var ix = gStateObject.windows.length - 1; - for (var t = gTreeData.length - 1; t >= 0; t--) { - if (treeView.isContainer(t)) { - if (gTreeData[t].checked === 0) - // this window will be restored partially - gStateObject.windows[ix].tabs = - gStateObject.windows[ix].tabs.filter(function(aTabData, aIx) - gTreeData[t].tabs[aIx].checked); - else if (!gTreeData[t].checked) - // this window won't be restored at all - gStateObject.windows.splice(ix, 1); - ix--; - } - } - var stateString = JSON.stringify(gStateObject); - - var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore); - var top = getBrowserWindow(); - - // if there's only this page open, reuse the window for restoring the session - if (top.gBrowser.tabs.length == 1) { - ss.setWindowState(top, stateString, true); - return; - } - - // restore the session into a new window and close the current tab - var newWindow = top.openDialog(top.location, "_blank", "chrome,dialog=no,all"); - newWindow.addEventListener("load", function() { - newWindow.removeEventListener("load", arguments.callee, true); - ss.setWindowState(newWindow, stateString, true); - - var tabbrowser = top.gBrowser; - var tabIndex = tabbrowser.getBrowserIndexForDocument(document); - tabbrowser.removeTab(tabbrowser.tabs[tabIndex]); - }, true); -} - -function startNewSession() { - var prefBranch = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); - if (prefBranch.getIntPref("browser.startup.page") == 0) - getBrowserWindow().gBrowser.loadURI("about:logopage"); - else - getBrowserWindow().BrowserHome(); -} - -function onListClick(aEvent) { - // don't react to right-clicks - if (aEvent.button == 2) - return; - - if (!treeView.treeBox) { - return; - } - var cell = treeView.treeBox.getCellAt(aEvent.clientX, aEvent.clientY); - if (cell.col) { - // Restore this specific tab in the same window for middle/double/accel clicking - // on a tab's title. -#ifdef XP_MACOSX - let accelKey = aEvent.metaKey; -#else - let accelKey = aEvent.ctrlKey; -#endif - if ((aEvent.button == 1 || aEvent.button == 0 && aEvent.detail == 2 || accelKey) && - cell.col.id == "title" && - !treeView.isContainer(cell.row)) { - restoreSingleTab(cell.row, aEvent.shiftKey); - aEvent.stopPropagation(); - } - else if (cell.col.id == "restore") - toggleRowChecked(cell.row); - } -} - -function onListKeyDown(aEvent) { - switch (aEvent.keyCode) - { - case KeyEvent.DOM_VK_SPACE: - toggleRowChecked(document.getElementById("tabList").currentIndex); - break; - case KeyEvent.DOM_VK_RETURN: - var ix = document.getElementById("tabList").currentIndex; - if (aEvent.ctrlKey && !treeView.isContainer(ix)) - restoreSingleTab(ix, aEvent.shiftKey); - break; - case KeyEvent.DOM_VK_UP: - case KeyEvent.DOM_VK_DOWN: - case KeyEvent.DOM_VK_PAGE_UP: - case KeyEvent.DOM_VK_PAGE_DOWN: - case KeyEvent.DOM_VK_HOME: - case KeyEvent.DOM_VK_END: - aEvent.preventDefault(); // else the page scrolls unwantedly - break; - } -} - -// Helper functions - -function getBrowserWindow() { - return window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShellTreeItem).rootTreeItem - .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow); -} - -function toggleRowChecked(aIx) { - var item = gTreeData[aIx]; - item.checked = !item.checked; - treeView.treeBox.invalidateRow(aIx); - - function isChecked(aItem) aItem.checked; - - if (treeView.isContainer(aIx)) { - // (un)check all tabs of this window as well - for (let tab of item.tabs) { - tab.checked = item.checked; - treeView.treeBox.invalidateRow(gTreeData.indexOf(tab)); - } - } - else { - // update the window's checkmark as well (0 means "partially checked") - item.parent.checked = item.parent.tabs.every(isChecked) ? true : - item.parent.tabs.some(isChecked) ? 0 : false; - treeView.treeBox.invalidateRow(gTreeData.indexOf(item.parent)); - } - - document.getElementById("errorTryAgain").disabled = !gTreeData.some(isChecked); -} - -function restoreSingleTab(aIx, aShifted) { - var tabbrowser = getBrowserWindow().gBrowser; - var newTab = tabbrowser.addTab(); - var item = gTreeData[aIx]; - - var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore); - var tabState = gStateObject.windows[item.parent.ix] - .tabs[aIx - gTreeData.indexOf(item.parent) - 1]; - // ensure tab would be visible on the tabstrip. - tabState.hidden = false; - ss.setTabState(newTab, JSON.stringify(tabState)); - - // respect the preference as to whether to select the tab (the Shift key inverses) - var prefBranch = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); - if (prefBranch.getBoolPref("browser.tabs.loadInBackground") != !aShifted) - tabbrowser.selectedTab = newTab; -} - -// Tree controller - -var treeView = { - treeBox: null, - selection: null, - - get rowCount() { return gTreeData.length; }, - setTree: function(treeBox) { this.treeBox = treeBox; }, - getCellText: function(idx, column) { return gTreeData[idx].label; }, - isContainer: function(idx) { return "open" in gTreeData[idx]; }, - getCellValue: function(idx, column){ return gTreeData[idx].checked; }, - isContainerOpen: function(idx) { return gTreeData[idx].open; }, - isContainerEmpty: function(idx) { return false; }, - isSeparator: function(idx) { return false; }, - isSorted: function() { return false; }, - isEditable: function(idx, column) { return false; }, - canDrop: function(idx, orientation, dt) { return false; }, - getLevel: function(idx) { return this.isContainer(idx) ? 0 : 1; }, - - getParentIndex: function(idx) { - if (!this.isContainer(idx)) - for (var t = idx - 1; t >= 0 ; t--) - if (this.isContainer(t)) - return t; - return -1; - }, - - hasNextSibling: function(idx, after) { - var thisLevel = this.getLevel(idx); - for (var t = after + 1; t < gTreeData.length; t++) - if (this.getLevel(t) <= thisLevel) - return this.getLevel(t) == thisLevel; - return false; - }, - - toggleOpenState: function(idx) { - if (!this.isContainer(idx)) - return; - var item = gTreeData[idx]; - if (item.open) { - // remove this window's tab rows from the view - var thisLevel = this.getLevel(idx); - for (var t = idx + 1; t < gTreeData.length && this.getLevel(t) > thisLevel; t++); - var deletecount = t - idx - 1; - gTreeData.splice(idx + 1, deletecount); - this.treeBox.rowCountChanged(idx + 1, -deletecount); - } - else { - // add this window's tab rows to the view - var toinsert = gTreeData[idx].tabs; - for (var i = 0; i < toinsert.length; i++) - gTreeData.splice(idx + i + 1, 0, toinsert[i]); - this.treeBox.rowCountChanged(idx + 1, toinsert.length); - } - item.open = !item.open; - this.treeBox.invalidateRow(idx); - }, - - getCellProperties: function(idx, column) { - if (column.id == "restore" && this.isContainer(idx) && gTreeData[idx].checked === 0) - return "partial"; - if (column.id == "title") - return this.getImageSrc(idx, column) ? "icon" : "noicon"; - - return ""; - }, - - getRowProperties: function(idx) { - var winState = gTreeData[idx].parent || gTreeData[idx]; - if (winState.ix % 2 != 0) - return "alternate"; - - return ""; - }, - - getImageSrc: function(idx, column) { - if (column.id == "title") - return gTreeData[idx].src || null; - return null; - }, - - getProgressMode : function(idx, column) { }, - cycleHeader: function(column) { }, - cycleCell: function(idx, column) { }, - selectionChanged: function() { }, - performAction: function(action) { }, - performActionOnCell: function(action, index, column) { }, - getColumnProperties: function(column) { return ""; } -}; diff --git a/components/sessionstore/content/aboutSessionRestore.xhtml b/components/sessionstore/content/aboutSessionRestore.xhtml deleted file mode 100644 index 6b22250..0000000 --- a/components/sessionstore/content/aboutSessionRestore.xhtml +++ /dev/null @@ -1,94 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. ---> -<!DOCTYPE html [ - <!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd"> - %htmlDTD; - <!ENTITY % netErrorDTD SYSTEM "chrome://global/locale/netError.dtd"> - %netErrorDTD; - <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd"> - %globalDTD; - <!ENTITY % restorepageDTD SYSTEM "chrome://browser/locale/aboutSessionRestore.dtd"> - %restorepageDTD; -]> - -<html xmlns="http://www.w3.org/1999/xhtml"> - <head> - <title>&restorepage.tabtitle;</title> - <link rel="stylesheet" href="chrome://global/skin/netError.css" type="text/css" media="all"/> - <link rel="stylesheet" href="chrome://browser/skin/aboutSessionRestore.css" type="text/css" media="all"/> - <link rel="icon" type="image/png" href="chrome://global/skin/icons/warning-16.png"/> - - <script type="application/javascript;version=1.8" src="chrome://browser/content/aboutSessionRestore.js"/> - </head> - - <body dir="&locale.dir;"> - - <!-- PAGE CONTAINER (for styling purposes only) --> - <div id="errorPageContainer"> - - <!-- Error Title --> - <div id="errorTitle"> - <h1 id="errorTitleText">&restorepage.errorTitle;</h1> - </div> - - <!-- LONG CONTENT (the section most likely to require scrolling) --> - <div id="errorLongContent"> - - <!-- Short Description --> - <div id="errorShortDesc"> - <p id="errorShortDescText">&restorepage.problemDesc;</p> - </div> - - <!-- Long Description (Note: See netError.dtd for used XHTML tags) --> - <div id="errorLongDesc"> - <p>&restorepage.tryThis;</p> - <ul> - <li>&restorepage.restoreSome;</li> - <li>&restorepage.startNew;</li> - </ul> - </div> - - <!-- Short Description --> - <div id="errorTrailerDesc"> - <tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - id="tabList" flex="1" seltype="single" hidecolumnpicker="true" - onclick="onListClick(event);" onkeydown="onListKeyDown(event);" - _window_label="&restorepage.windowLabel;"> - <treecols> - <treecol cycler="true" id="restore" type="checkbox" label="&restorepage.restoreHeader;"/> - <splitter class="tree-splitter"/> - <treecol primary="true" id="title" label="&restorepage.listHeader;" flex="1"/> - </treecols> - <treechildren flex="1"/> - </tree> - </div> - </div> - - <!-- Buttons --> - <hbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="buttons"> -#ifdef XP_UNIX - <button id="errorCancel" label="&restorepage.closeButton;" - accesskey="&restorepage.close.access;" - oncommand="startNewSession();"/> - <button id="errorTryAgain" label="&restorepage.tryagainButton;" - accesskey="&restorepage.restore.access;" - oncommand="restoreSession();"/> -#else - <button id="errorTryAgain" label="&restorepage.tryagainButton;" - accesskey="&restorepage.restore.access;" - oncommand="restoreSession();"/> - <button id="errorCancel" label="&restorepage.closeButton;" - accesskey="&restorepage.close.access;" - oncommand="startNewSession();"/> -#endif - </hbox> - <!-- holds the session data for when the tab is closed --> - <input type="text" id="sessionData" style="display: none;"/> - </div> - - </body> -</html> diff --git a/components/sessionstore/content/content-sessionStore.js b/components/sessionstore/content/content-sessionStore.js deleted file mode 100644 index e3e956e..0000000 --- a/components/sessionstore/content/content-sessionStore.js +++ /dev/null @@ -1,40 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -function debug(msg) { - Services.console.logStringMessage("SessionStoreContent: " + msg); -} - -/** - * Listens for and handles content events that we need for the - * session store service to be notified of state changes in content. - */ -var EventListener = { - - DOM_EVENTS: [ - "pageshow", "change", "input" - ], - - init: function () { - this.DOM_EVENTS.forEach(e => addEventListener(e, this, true)); - }, - - handleEvent: function (event) { - switch (event.type) { - case "pageshow": - if (event.persisted) - sendAsyncMessage("SessionStore:pageshow"); - break; - case "input": - case "change": - sendAsyncMessage("SessionStore:input"); - break; - default: - debug("received unknown event '" + event.type + "'"); - break; - } - } -}; - -EventListener.init(); diff --git a/components/sessionstore/jar.mn b/components/sessionstore/jar.mn deleted file mode 100644 index 825b00f..0000000 --- a/components/sessionstore/jar.mn +++ /dev/null @@ -1,8 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -browser.jar: -* content/browser/aboutSessionRestore.xhtml (content/aboutSessionRestore.xhtml) -* content/browser/aboutSessionRestore.js (content/aboutSessionRestore.js) - content/browser/content-sessionStore.js (content/content-sessionStore.js) diff --git a/components/sessionstore/moz.build b/components/sessionstore/moz.build deleted file mode 100644 index 84278da..0000000 --- a/components/sessionstore/moz.build +++ /dev/null @@ -1,29 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -JAR_MANIFESTS += ['jar.mn'] - -XPIDL_SOURCES += [ - 'nsISessionStartup.idl', - 'nsISessionStore.idl', -] - -XPIDL_MODULE = 'sessionstore' - -EXTRA_COMPONENTS += [ - 'nsSessionStartup.js', - 'nsSessionStore.js', - 'nsSessionStore.manifest', -] - -EXTRA_JS_MODULES.sessionstore = [ - '_SessionFile.jsm', - 'DocumentUtils.jsm', - 'SessionStorage.jsm', - 'XPathGenerator.jsm', -] - -EXTRA_PP_JS_MODULES.sessionstore += ['SessionStore.jsm']
\ No newline at end of file diff --git a/components/sessionstore/nsISessionStartup.idl b/components/sessionstore/nsISessionStartup.idl deleted file mode 100644 index a8e786d..0000000 --- a/components/sessionstore/nsISessionStartup.idl +++ /dev/null @@ -1,59 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" - -/** - * nsISessionStore keeps track of the current browsing state - i.e. - * tab history, cookies, scroll state, form data, POSTDATA and window features - * - and allows to restore everything into one window. - */ - -[scriptable, uuid(51f4b9f0-f3d2-11e2-bb62-2c24dd830245)] -interface nsISessionStartup: nsISupports -{ - /** - * Return a promise that is resolved once initialization - * is complete. - */ - readonly attribute jsval onceInitialized; - - // Get session state - readonly attribute jsval state; - - /** - * Determines whether there is a pending session restore and makes sure that - * we're initialized before returning. If we're not yet this will read the - * session file synchronously. - */ - boolean doRestore(); - - /** - * Returns whether we will restore a session that ends up replacing the - * homepage. The browser uses this to not start loading the homepage if - * we're going to stop its load anyway shortly after. - * - * This is meant to be an optimization for the average case that loading the - * session file finishes before we may want to start loading the default - * homepage. Should this be called before the session file has been read it - * will just return false. - */ - readonly attribute bool willOverrideHomepage; - - /** - * What type of session we're restoring. - * NO_SESSION There is no data available from the previous session - * RECOVER_SESSION The last session crashed. It will either be restored or - * about:sessionrestore will be shown. - * RESUME_SESSION The previous session should be restored at startup - * DEFER_SESSION The previous session is fine, but it shouldn't be restored - * without explicit action (with the exception of pinned tabs) - */ - const unsigned long NO_SESSION = 0; - const unsigned long RECOVER_SESSION = 1; - const unsigned long RESUME_SESSION = 2; - const unsigned long DEFER_SESSION = 3; - - readonly attribute unsigned long sessionType; -}; diff --git a/components/sessionstore/nsISessionStore.idl b/components/sessionstore/nsISessionStore.idl deleted file mode 100644 index 0490772..0000000 --- a/components/sessionstore/nsISessionStore.idl +++ /dev/null @@ -1,206 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" - -interface nsIDOMWindow; -interface nsIDOMNode; - -/** - * nsISessionStore keeps track of the current browsing state - i.e. - * tab history, cookies, scroll state, form data, POSTDATA and window features - * - and allows to restore everything into one browser window. - * - * The nsISessionStore API operates mostly on browser windows and the tabbrowser - * tabs contained in them: - * - * * "Browser windows" are those DOM windows having loaded - * chrome://browser/content/browser.xul . From overlays you can just pass the - * global |window| object to the API, though (or |top| from a sidebar). - * From elsewhere you can get browser windows through the nsIWindowMediator - * by looking for "navigator:browser" windows. - * - * * "Tabbrowser tabs" are all the child nodes of a browser window's - * |gBrowser.tabContainer| such as e.g. |gBrowser.selectedTab|. - */ - -[scriptable, uuid(43ec216b-f002-4424-bfc5-fc555c87dbc4)] -interface nsISessionStore : nsISupports -{ - /** - * Initialize the service - */ - jsval init(in nsIDOMWindow aWindow); - - /** - * Is it possible to restore the previous session. Will always be false when - * in Private Browsing mode. - */ - attribute boolean canRestoreLastSession; - - /** - * Restore the previous session if possible. This will not overwrite the - * current session. Instead the previous session will be merged into the - * current session. Current windows will be reused if they were windows that - * pinned tabs were previously restored into. New windows will be opened as - * needed. - * - * Note: This will throw if there is no previous state to restore. Check with - * canRestoreLastSession first to avoid thrown errors. - */ - void restoreLastSession(); - - /** - * Get the current browsing state. - * @returns a JSON string representing the session state. - */ - AString getBrowserState(); - - /** - * Set the browsing state. - * This will immediately restore the state of the whole application to the state - * passed in, *replacing* the current session. - * - * @param aState is a JSON string representing the session state. - */ - void setBrowserState(in AString aState); - - /** - * @param aWindow is the browser window whose state is to be returned. - * - * @returns a JSON string representing a session state with only one window. - */ - AString getWindowState(in nsIDOMWindow aWindow); - - /** - * @param aWindow is the browser window whose state is to be set. - * @param aState is a JSON string representing a session state. - * @param aOverwrite boolean overwrite existing tabs - */ - void setWindowState(in nsIDOMWindow aWindow, in AString aState, in boolean aOverwrite); - - /** - * @param aTab is the tabbrowser tab whose state is to be returned. - * - * @returns a JSON string representing the state of the tab - * (note: doesn't contain cookies - if you need them, use getWindowState instead). - */ - AString getTabState(in nsIDOMNode aTab); - - /** - * @param aTab is the tabbrowser tab whose state is to be set. - * @param aState is a JSON string representing a session state. - */ - void setTabState(in nsIDOMNode aTab, in AString aState); - - /** - * Duplicates a given tab as thoroughly as possible. - * - * @param aWindow is the browser window into which the tab will be duplicated. - * @param aTab is the tabbrowser tab to duplicate (can be from a different window). - * @param aDelta is the offset to the history entry to load in the duplicated tab. - * @returns a reference to the newly created tab. - */ - nsIDOMNode duplicateTab(in nsIDOMWindow aWindow, in nsIDOMNode aTab, - [optional] in long aDelta); - - /** - * Get the number of restore-able tabs for a browser window - */ - unsigned long getClosedTabCount(in nsIDOMWindow aWindow); - - /** - * Get closed tab data - * - * @param aWindow is the browser window for which to get closed tab data - * @returns a JSON string representing the list of closed tabs. - */ - AString getClosedTabData(in nsIDOMWindow aWindow); - - /** - * @param aWindow is the browser window to reopen a closed tab in. - * @param aIndex is the index of the tab to be restored (FIFO ordered). - * @returns a reference to the reopened tab. - */ - nsIDOMNode undoCloseTab(in nsIDOMWindow aWindow, in unsigned long aIndex); - - /** - * @param aWindow is the browser window associated with the closed tab. - * @param aIndex is the index of the closed tab to be removed (FIFO ordered). - */ - nsIDOMNode forgetClosedTab(in nsIDOMWindow aWindow, in unsigned long aIndex); - - /** - * Get the number of restore-able windows - */ - unsigned long getClosedWindowCount(); - - /** - * Get closed windows data - * - * @returns a JSON string representing the list of closed windows. - */ - AString getClosedWindowData(); - - /** - * @param aIndex is the index of the windows to be restored (FIFO ordered). - * @returns the nsIDOMWindow object of the reopened window - */ - nsIDOMWindow undoCloseWindow(in unsigned long aIndex); - - /** - * @param aIndex is the index of the closed window to be removed (FIFO ordered). - * - * @throws NS_ERROR_INVALID_ARG - * when aIndex does not map to a closed window - */ - nsIDOMNode forgetClosedWindow(in unsigned long aIndex); - - /** - * @param aWindow is the window to get the value for. - * @param aKey is the value's name. - * - * @returns A string value or an empty string if none is set. - */ - AString getWindowValue(in nsIDOMWindow aWindow, in AString aKey); - - /** - * @param aWindow is the browser window to set the value for. - * @param aKey is the value's name. - * @param aStringValue is the value itself (use JSON.stringify/parse before setting JS objects). - */ - void setWindowValue(in nsIDOMWindow aWindow, in AString aKey, in AString aStringValue); - - /** - * @param aWindow is the browser window to get the value for. - * @param aKey is the value's name. - */ - void deleteWindowValue(in nsIDOMWindow aWindow, in AString aKey); - - /** - * @param aTab is the tabbrowser tab to get the value for. - * @param aKey is the value's name. - * - * @returns A string value or an empty string if none is set. - */ - AString getTabValue(in nsIDOMNode aTab, in AString aKey); - - /** - * @param aTab is the tabbrowser tab to set the value for. - * @param aKey is the value's name. - * @param aStringValue is the value itself (use JSON.stringify/parse before setting JS objects). - */ - void setTabValue(in nsIDOMNode aTab, in AString aKey, in AString aStringValue); - - /** - * @param aTab is the tabbrowser tab to get the value for. - * @param aKey is the value's name. - */ - void deleteTabValue(in nsIDOMNode aTab, in AString aKey); - - /** - * @param aName is the name of the attribute to save/restore for all tabbrowser tabs. - */ - void persistTabAttribute(in AString aName); -}; diff --git a/components/sessionstore/nsSessionStartup.js b/components/sessionstore/nsSessionStartup.js deleted file mode 100644 index 04037c1..0000000 --- a/components/sessionstore/nsSessionStartup.js +++ /dev/null @@ -1,296 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Session Storage and Restoration - * - * Overview - * This service reads user's session file at startup, and makes a determination - * as to whether the session should be restored. It will restore the session - * under the circumstances described below. If the auto-start Private Browsing - * mode is active, however, the session is never restored. - * - * Crash Detection - * The session file stores a session.state property, that - * indicates whether the browser is currently running. When the browser shuts - * down, the field is changed to "stopped". At startup, this field is read, and - * if its value is "running", then it's assumed that the browser had previously - * crashed, or at the very least that something bad happened, and that we should - * restore the session. - * - * Forced Restarts - * In the event that a restart is required due to application update or extension - * installation, set the browser.sessionstore.resume_session_once pref to true, - * and the session will be restored the next time the browser starts. - * - * Always Resume - * This service will always resume the session if the integer pref - * browser.startup.page is set to 3. - */ - -/* :::::::: Constants and Helpers ::::::::::::::: */ - -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cr = Components.results; -const Cu = Components.utils; -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); -Cu.import("resource://gre/modules/Promise.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "_SessionFile", - "resource:///modules/sessionstore/_SessionFile.jsm"); - -const STATE_RUNNING_STR = "running"; - -function debug(aMsg) { - aMsg = ("SessionStartup: " + aMsg).replace(/\S{80}/g, "$&\n"); - Services.console.logStringMessage(aMsg); -} - -var gOnceInitializedDeferred = Promise.defer(); - -/* :::::::: The Service ::::::::::::::: */ - -function SessionStartup() { -} - -SessionStartup.prototype = { - - // the state to restore at startup - _initialState: null, - _sessionType: Ci.nsISessionStartup.NO_SESSION, - _initialized: false, - -/* ........ Global Event Handlers .............. */ - - /** - * Initialize the component - */ - init: function sss_init() { - // do not need to initialize anything in auto-started private browsing sessions - if (PrivateBrowsingUtils.permanentPrivateBrowsing) { - this._initialized = true; - gOnceInitializedDeferred.resolve(); - return; - } - - if (Services.prefs.getBoolPref("browser.sessionstore.resume_session_once") || - Services.prefs.getIntPref("browser.startup.page") == 3) { - this._ensureInitialized(); - } else { - _SessionFile.read().then( - this._onSessionFileRead.bind(this) - ); - } - }, - - // Wrap a string as a nsISupports - _createSupportsString: function ssfi_createSupportsString(aData) { - let string = Cc["@mozilla.org/supports-string;1"] - .createInstance(Ci.nsISupportsString); - string.data = aData; - return string; - }, - - _onSessionFileRead: function sss_onSessionFileRead(aStateString) { - if (this._initialized) { - // Initialization is complete, nothing else to do - return; - } - try { - this._initialized = true; - - // Let observers modify the state before it is used - let supportsStateString = this._createSupportsString(aStateString); - Services.obs.notifyObservers(supportsStateString, "sessionstore-state-read", ""); - aStateString = supportsStateString.data; - - // No valid session found. - if (!aStateString) { - this._sessionType = Ci.nsISessionStartup.NO_SESSION; - return; - } - - // parse the session state into a JS object - // remove unneeded braces (added for compatibility with Firefox 2.0 and 3.0) - if (aStateString.charAt(0) == '(') - aStateString = aStateString.slice(1, -1); - let corruptFile = false; - try { - this._initialState = JSON.parse(aStateString); - } - catch (ex) { - debug("The session file contained un-parse-able JSON: " + ex); - // This is not valid JSON, but this might still be valid JavaScript, - // as used in FF2/FF3, so we need to eval. - // evalInSandbox will throw if aStateString is not parse-able. - try { - var s = new Cu.Sandbox("about:blank", {sandboxName: 'nsSessionStartup'}); - this._initialState = Cu.evalInSandbox("(" + aStateString + ")", s); - } catch(ex) { - debug("The session file contained un-eval-able JSON: " + ex); - corruptFile = true; - } - } - let doResumeSessionOnce = Services.prefs.getBoolPref("browser.sessionstore.resume_session_once"); - let doResumeSession = doResumeSessionOnce || - Services.prefs.getIntPref("browser.startup.page") == 3; - - // If this is a normal restore then throw away any previous session - if (!doResumeSessionOnce) - delete this._initialState.lastSessionState; - - let resumeFromCrash = Services.prefs.getBoolPref("browser.sessionstore.resume_from_crash"); - let lastSessionCrashed = - this._initialState && this._initialState.session && - this._initialState.session.state && - this._initialState.session.state == STATE_RUNNING_STR; - - // set the startup type - if (lastSessionCrashed && resumeFromCrash) - this._sessionType = Ci.nsISessionStartup.RECOVER_SESSION; - else if (!lastSessionCrashed && doResumeSession) - this._sessionType = Ci.nsISessionStartup.RESUME_SESSION; - else if (this._initialState) - this._sessionType = Ci.nsISessionStartup.DEFER_SESSION; - else - this._initialState = null; // reset the state - - Services.obs.addObserver(this, "sessionstore-windows-restored", true); - - if (this._sessionType != Ci.nsISessionStartup.NO_SESSION) - Services.obs.addObserver(this, "browser:purge-session-history", true); - - } finally { - // We're ready. Notify everyone else. - Services.obs.notifyObservers(null, "sessionstore-state-finalized", ""); - gOnceInitializedDeferred.resolve(); - } - }, - - /** - * Handle notifications - */ - observe: function sss_observe(aSubject, aTopic, aData) { - switch (aTopic) { - case "app-startup": - Services.obs.addObserver(this, "final-ui-startup", true); - Services.obs.addObserver(this, "quit-application", true); - break; - case "final-ui-startup": - Services.obs.removeObserver(this, "final-ui-startup"); - Services.obs.removeObserver(this, "quit-application"); - this.init(); - break; - case "quit-application": - // no reason for initializing at this point (cf. bug 409115) - Services.obs.removeObserver(this, "final-ui-startup"); - Services.obs.removeObserver(this, "quit-application"); - if (this._sessionType != Ci.nsISessionStartup.NO_SESSION) - Services.obs.removeObserver(this, "browser:purge-session-history"); - break; - case "sessionstore-windows-restored": - Services.obs.removeObserver(this, "sessionstore-windows-restored"); - // free _initialState after nsSessionStore is done with it - this._initialState = null; - break; - case "browser:purge-session-history": - Services.obs.removeObserver(this, "browser:purge-session-history"); - // reset all state on sanitization - this._sessionType = Ci.nsISessionStartup.NO_SESSION; - break; - } - }, - -/* ........ Public API ................*/ - - get onceInitialized() { - return gOnceInitializedDeferred.promise; - }, - - /** - * Get the session state as a jsval - */ - get state() { - this._ensureInitialized(); - return this._initialState; - }, - - /** - * Determines whether there is a pending session restore and makes sure that - * we're initialized before returning. If we're not yet this will read the - * session file synchronously. - * @returns bool - */ - doRestore: function sss_doRestore() { - this._ensureInitialized(); - return this._willRestore(); - }, - - /** - * Determines whether there is a pending session restore. - * @returns bool - */ - _willRestore: function () { - return this._sessionType == Ci.nsISessionStartup.RECOVER_SESSION || - this._sessionType == Ci.nsISessionStartup.RESUME_SESSION; - }, - - /** - * Returns whether we will restore a session that ends up replacing the - * homepage. The browser uses this to not start loading the homepage if - * we're going to stop its load anyway shortly after. - * - * This is meant to be an optimization for the average case that loading the - * session file finishes before we may want to start loading the default - * homepage. Should this be called before the session file has been read it - * will just return false. - * - * @returns bool - */ - get willOverrideHomepage() { - if (this._initialState && this._willRestore()) { - let windows = this._initialState.windows || null; - // If there are valid windows with not only pinned tabs, signal that we - // will override the default homepage by restoring a session. - return windows && windows.some(w => w.tabs.some(t => !t.pinned)); - } - return false; - }, - - /** - * Get the type of pending session store, if any. - */ - get sessionType() { - this._ensureInitialized(); - return this._sessionType; - }, - - // Ensure that initialization is complete. - // If initialization is not complete yet, fall back to a synchronous - // initialization and kill ongoing asynchronous initialization - _ensureInitialized: function sss__ensureInitialized() { - try { - if (this._initialized) { - // Initialization is complete, nothing else to do - return; - } - let contents = _SessionFile.syncRead(); - this._onSessionFileRead(contents); - } catch(ex) { - debug("ensureInitialized: could not read session " + ex + ", " + ex.stack); - throw ex; - } - }, - - /* ........ QueryInterface .............. */ - QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver, - Ci.nsISupportsWeakReference, - Ci.nsISessionStartup]), - classID: Components.ID("{ec7a6c20-e081-11da-8ad9-0800200c9a66}") -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SessionStartup]); diff --git a/components/sessionstore/nsSessionStore.js b/components/sessionstore/nsSessionStore.js deleted file mode 100644 index 38713d5..0000000 --- a/components/sessionstore/nsSessionStore.js +++ /dev/null @@ -1,37 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Session Storage and Restoration - * - * Overview - * This service keeps track of a user's session, storing the various bits - * required to return the browser to its current state. The relevant data is - * stored in memory, and is periodically saved to disk in a file in the - * profile directory. The service is started at first window load, in - * delayedStartup, and will restore the session from the data received from - * the nsSessionStartup service. - */ - -const Cu = Components.utils; -const Ci = Components.interfaces; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource:///modules/sessionstore/SessionStore.jsm"); - -function SessionStoreService() {} - -// The SessionStore module's object is frozen. We need to modify our prototype -// and add some properties so let's just copy the SessionStore object. -Object.keys(SessionStore).forEach(function (aName) { - let desc = Object.getOwnPropertyDescriptor(SessionStore, aName); - Object.defineProperty(SessionStoreService.prototype, aName, desc); -}); - -SessionStoreService.prototype.classID = - Components.ID("{5280606b-2510-4fe0-97ef-9b5a22eafe6b}"); -SessionStoreService.prototype.QueryInterface = - XPCOMUtils.generateQI([Ci.nsISessionStore]); - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SessionStoreService]); diff --git a/components/sessionstore/nsSessionStore.manifest b/components/sessionstore/nsSessionStore.manifest deleted file mode 100644 index b136b41..0000000 --- a/components/sessionstore/nsSessionStore.manifest +++ /dev/null @@ -1,18 +0,0 @@ -# WebappRT doesn't need these instructions, and they don't necessarily work -# with it, but it does use a GRE directory that the GRE shares with Firefox, -# so in order to prevent the instructions from being processed for WebappRT, -# we need to restrict them to the applications that depend on them, i.e.: -# -# b2g: {3c2e2abc-06d4-11e1-ac3b-374f68613e61} -# browser: {8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} -# mobile/android: {aa3c5121-dab2-40e2-81ca-7ea25febc110} -# mobile/xul: {a23983c0-fd0e-11dc-95ff-0800200c9a66} -# -# In theory we should do this for all these instructions, but in practice it is -# sufficient to do it for the app-startup one, and the file is simpler that way. - -component {5280606b-2510-4fe0-97ef-9b5a22eafe6b} nsSessionStore.js -contract @mozilla.org/browser/sessionstore;1 {5280606b-2510-4fe0-97ef-9b5a22eafe6b} -component {ec7a6c20-e081-11da-8ad9-0800200c9a66} nsSessionStartup.js -contract @mozilla.org/browser/sessionstartup;1 {ec7a6c20-e081-11da-8ad9-0800200c9a66} -category app-startup nsSessionStartup service,@mozilla.org/browser/sessionstartup;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66} diff --git a/components/shell/ShellService.jsm b/components/shell/ShellService.jsm deleted file mode 100644 index 74632b6..0000000 --- a/components/shell/ShellService.jsm +++ /dev/null @@ -1,114 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public -* License, v. 2.0. If a copy of the MPL was not distributed with this file, -* You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -this.EXPORTED_SYMBOLS = ["ShellService"]; - -const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; - -Cu.import("resource://gre/modules/AppConstants.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry", - "resource://gre/modules/WindowsRegistry.jsm"); - -/** - * Internal functionality to save and restore the docShell.allow* properties. - */ -var ShellServiceInternal = { - /** - * Used to determine whether or not to offer "Set as desktop background" - * functionality. Even if shell service is available it is not - * guaranteed that it is able to set the background for every desktop - * which is especially true for Linux with its many different desktop - * environments. - */ - get canSetDesktopBackground() { - if (AppConstants.platform == "win" || - AppConstants.platform == "macosx") { - return true; - } - - if (AppConstants.platform == "linux") { - if (this.shellService) { - let linuxShellService = this.shellService - .QueryInterface(Ci.nsIGNOMEShellService); - return linuxShellService.canSetDesktopBackground; - } - } - - return false; - }, - - /** - * Used to determine whether or not to show a "Set Default Browser" - * query dialog. This attribute is true if the application is starting - * up and "browser.shell.checkDefaultBrowser" is true, otherwise it - * is false. - */ - _checkedThisSession: false, - get shouldCheckDefaultBrowser() { - // If we've already checked, the browser has been started and this is a - // new window open, and we don't want to check again. - if (this._checkedThisSession) { - return false; - } - - if (!Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser")) { - return false; - } - - if (AppConstants.platform == "win") { - let optOutValue = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, - "Software\\Mozilla\\PaleMoon", - "DefaultBrowserOptOut"); - WindowsRegistry.removeRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, - "Software\\Mozilla\\PaleMoon", - "DefaultBrowserOptOut"); - if (optOutValue == "True") { - Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", false); - return false; - } - } - - return true; - }, - - set shouldCheckDefaultBrowser(shouldCheck) { - Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", !!shouldCheck); - }, - - isDefaultBrowser(startupCheck, forAllTypes) { - // If this is the first browser window, maintain internal state that we've - // checked this session (so that subsequent window opens don't show the - // default browser dialog). - if (startupCheck) { - this._checkedThisSession = true; - } - if (this.shellService) { - return this.shellService.isDefaultBrowser(startupCheck, forAllTypes); - } - return false; - } -}; - -XPCOMUtils.defineLazyServiceGetter(ShellServiceInternal, "shellService", - "@mozilla.org/browser/shell-service;1", Ci.nsIShellService); - -/** - * The external API exported by this module. - */ -this.ShellService = new Proxy(ShellServiceInternal, { - get(target, name) { - if (name in target) { - return target[name]; - } - if (target.shellService) { - return target.shellService[name]; - } - Services.console.logStringMessage(`${name} not found in ShellService: ${target.shellService}`); - return undefined; - } -}); diff --git a/components/shell/content/setDesktopBackground.js b/components/shell/content/setDesktopBackground.js deleted file mode 100644 index 53cc70d..0000000 --- a/components/shell/content/setDesktopBackground.js +++ /dev/null @@ -1,214 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -Components.utils.import("resource://gre/modules/AppConstants.jsm"); - -var Ci = Components.interfaces; - -var gSetBackground = { - _position : AppConstants.platform == "macosx" ? "STRETCH" : "", - _backgroundColor : AppConstants.platform != "macosx" ? 0 : undefined, - _screenWidth : 0, - _screenHeight : 0, - _image : null, - _canvas : null, - - get _shell() - { - return Components.classes["@mozilla.org/browser/shell-service;1"] - .getService(Ci.nsIShellService); - }, - - load: function () - { - this._canvas = document.getElementById("screen"); - this._screenWidth = screen.width; - this._screenHeight = screen.height; - if (AppConstants.platform == "macosx") { - document.documentElement.getButton("accept").hidden = true; - } - if (this._screenWidth / this._screenHeight >= 1.6) - document.getElementById("monitor").setAttribute("aspectratio", "16:10"); - - if (AppConstants.platform == "win") { - // Hide fill + fit options if < Win7 since they don't work. - var version = Components.classes["@mozilla.org/system-info;1"] - .getService(Ci.nsIPropertyBag2) - .getProperty("version"); - var isWindows7OrHigher = (parseFloat(version) >= 6.1); - if (!isWindows7OrHigher) { - document.getElementById("fillPosition").hidden = true; - document.getElementById("fitPosition").hidden = true; - } - } - - // make sure that the correct dimensions will be used - setTimeout(function(self) { - self.init(window.arguments[0]); - }, 0, this); - }, - - init: function (aImage) - { - this._image = aImage; - - // set the size of the coordinate space - this._canvas.width = this._canvas.clientWidth; - this._canvas.height = this._canvas.clientHeight; - - var ctx = this._canvas.getContext("2d"); - ctx.scale(this._canvas.clientWidth / this._screenWidth, this._canvas.clientHeight / this._screenHeight); - - if (AppConstants.platform != "macosx") { - this._initColor(); - } else { - // Make sure to reset the button state in case the user has already - // set an image as their desktop background. - var setDesktopBackground = document.getElementById("setDesktopBackground"); - setDesktopBackground.hidden = false; - var bundle = document.getElementById("backgroundBundle"); - setDesktopBackground.label = bundle.getString("DesktopBackgroundSet"); - setDesktopBackground.disabled = false; - - document.getElementById("showDesktopPreferences").hidden = true; - } - this.updatePosition(); - }, - - setDesktopBackground: function () - { - if (AppConstants.platform != "macosx") { - document.persist("menuPosition", "value"); - this._shell.desktopBackgroundColor = this._hexStringToLong(this._backgroundColor); - } else { - Components.classes["@mozilla.org/observer-service;1"] - .getService(Ci.nsIObserverService) - .addObserver(this, "shell:desktop-background-changed", false); - - var bundle = document.getElementById("backgroundBundle"); - var setDesktopBackground = document.getElementById("setDesktopBackground"); - setDesktopBackground.disabled = true; - setDesktopBackground.label = bundle.getString("DesktopBackgroundDownloading"); - } - this._shell.setDesktopBackground(this._image, - Ci.nsIShellService["BACKGROUND_" + this._position]); - }, - - updatePosition: function () - { - var ctx = this._canvas.getContext("2d"); - ctx.clearRect(0, 0, this._screenWidth, this._screenHeight); - - if (AppConstants.platform != "macosx") { - this._position = document.getElementById("menuPosition").value; - } - - switch (this._position) { - case "TILE": - ctx.save(); - ctx.fillStyle = ctx.createPattern(this._image, "repeat"); - ctx.fillRect(0, 0, this._screenWidth, this._screenHeight); - ctx.restore(); - break; - case "STRETCH": - ctx.drawImage(this._image, 0, 0, this._screenWidth, this._screenHeight); - break; - case "CENTER": { - let x = (this._screenWidth - this._image.naturalWidth) / 2; - let y = (this._screenHeight - this._image.naturalHeight) / 2; - ctx.drawImage(this._image, x, y); - break; - } - case "FILL": { - // Try maxing width first, overflow height. - let widthRatio = this._screenWidth / this._image.naturalWidth; - let width = this._image.naturalWidth * widthRatio; - let height = this._image.naturalHeight * widthRatio; - if (height < this._screenHeight) { - // Height less than screen, max height and overflow width. - let heightRatio = this._screenHeight / this._image.naturalHeight; - width = this._image.naturalWidth * heightRatio; - height = this._image.naturalHeight * heightRatio; - } - let x = (this._screenWidth - width) / 2; - let y = (this._screenHeight - height) / 2; - ctx.drawImage(this._image, x, y, width, height); - break; - } - case "FIT": { - // Try maxing width first, top and bottom borders. - let widthRatio = this._screenWidth / this._image.naturalWidth; - let width = this._image.naturalWidth * widthRatio; - let height = this._image.naturalHeight * widthRatio; - let x = 0; - let y = (this._screenHeight - height) / 2; - if (height > this._screenHeight) { - // Height overflow, maximise height, side borders. - let heightRatio = this._screenHeight / this._image.naturalHeight; - width = this._image.naturalWidth * heightRatio; - height = this._image.naturalHeight * heightRatio; - x = (this._screenWidth - width) / 2; - y = 0; - } - ctx.drawImage(this._image, x, y, width, height); - break; - } - } - } -}; - -if (AppConstants.platform != "macosx") { - gSetBackground["_initColor"] = function () - { - var color = this._shell.desktopBackgroundColor; - - const rMask = 4294901760; - const gMask = 65280; - const bMask = 255; - var r = (color & rMask) >> 16; - var g = (color & gMask) >> 8; - var b = (color & bMask); - this.updateColor(this._rgbToHex(r, g, b)); - - var colorpicker = document.getElementById("desktopColor"); - colorpicker.color = this._backgroundColor; - }; - - gSetBackground["updateColor"] = function (aColor) - { - this._backgroundColor = aColor; - this._canvas.style.backgroundColor = aColor; - }; - - // Converts a color string in the format "#RRGGBB" to an integer. - gSetBackground["_hexStringToLong"] = function (aString) - { - return parseInt(aString.substring(1, 3), 16) << 16 | - parseInt(aString.substring(3, 5), 16) << 8 | - parseInt(aString.substring(5, 7), 16); - }; - - gSetBackground["_rgbToHex"] = function (aR, aG, aB) - { - return "#" + [aR, aG, aB].map(aInt => aInt.toString(16).replace(/^(.)$/, "0$1")) - .join("").toUpperCase(); - }; -} else { - gSetBackground["observe"] = function (aSubject, aTopic, aData) - { - if (aTopic == "shell:desktop-background-changed") { - document.getElementById("setDesktopBackground").hidden = true; - document.getElementById("showDesktopPreferences").hidden = false; - - Components.classes["@mozilla.org/observer-service;1"] - .getService(Ci.nsIObserverService) - .removeObserver(this, "shell:desktop-background-changed"); - } - }; - - gSetBackground["showDesktopPrefs"] = function() - { - this._shell.openApplication(Ci.nsIMacShellService.APPLICATION_DESKTOP); - }; -} diff --git a/components/shell/content/setDesktopBackground.xul b/components/shell/content/setDesktopBackground.xul deleted file mode 100644 index d7d4079..0000000 --- a/components/shell/content/setDesktopBackground.xul +++ /dev/null @@ -1,84 +0,0 @@ -<?xml version="1.0"?> <!-- -*- Mode: HTML -*- --> - -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> -<?xml-stylesheet href="chrome://browser/skin/setDesktopBackground.css" type="text/css"?> - -<!DOCTYPE dialog SYSTEM "chrome://browser/locale/setDesktopBackground.dtd"> - -#ifdef XP_MACOSX -<?xul-overlay href="chrome://browser/content/macBrowserOverlay.xul"?> -#endif - -<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:html="http://www.w3.org/1999/xhtml" - windowtype="Shell:SetDesktopBackground" -#ifndef XP_MACOSX - buttons="accept,cancel" -#else - buttons="accept" -#endif - buttonlabelaccept="&setDesktopBackground.title;" - onload="gSetBackground.load();" - ondialogaccept="gSetBackground.setDesktopBackground();" - title="&setDesktopBackground.title;" - style="width: 30em;"> - - <stringbundle id="backgroundBundle" - src="chrome://browser/locale/shellservice.properties"/> - <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/> - <script type="application/javascript" src="chrome://browser/content/setDesktopBackground.js"/> - <script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/> - -#ifndef XP_MACOSX - <hbox align="center"> - <label value="&position.label;"/> - <menulist id="menuPosition" - label="&position.label;" - oncommand="gSetBackground.updatePosition();"> - <menupopup> - <menuitem label="¢er.label;" value="CENTER"/> - <menuitem label="&tile.label;" value="TILE"/> - <menuitem label="&stretch.label;" value="STRETCH"/> - <menuitem label="&fill.label;" value="FILL" id="fillPosition"/> - <menuitem label="&fit.label;" value="FIT" id="fitPosition"/> - </menupopup> - </menulist> - <spacer flex="1"/> - <label value="&color.label;"/> - <colorpicker id="desktopColor" - type="button" - onchange="gSetBackground.updateColor(this.color);"/> - </hbox> -#endif - <groupbox align="center"> - <caption label="&preview.label;"/> - <stack> - <!-- if width and height are not present, they default to 300x150 and stretch the stack --> - <html:canvas id="screen" width="1" height="1"/> - <image id="monitor"/> - </stack> - </groupbox> - -#ifdef XP_MACOSX - <separator/> - - <hbox align="right"> - <button id="setDesktopBackground" - label="&setDesktopBackground.title;" - oncommand="gSetBackground.setDesktopBackground();"/> - <button id="showDesktopPreferences" - label="&openDesktopPrefs.label;" - oncommand="gSetBackground.showDesktopPrefs();" - hidden="true"/> - </hbox> -#endif - -#ifdef XP_MACOSX -#include ../../../base/content/browserMountPoints.inc -#endif - -</dialog> diff --git a/components/shell/jar.mn b/components/shell/jar.mn deleted file mode 100644 index 0864e1b..0000000 --- a/components/shell/jar.mn +++ /dev/null @@ -1,7 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -browser.jar: -* content/browser/setDesktopBackground.xul (content/setDesktopBackground.xul) - content/browser/setDesktopBackground.js (content/setDesktopBackground.js) diff --git a/components/shell/moz.build b/components/shell/moz.build deleted file mode 100644 index 16bffd7..0000000 --- a/components/shell/moz.build +++ /dev/null @@ -1,40 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -JAR_MANIFESTS += ['jar.mn'] - -XPIDL_SOURCES += ['nsIShellService.idl'] - -if CONFIG['OS_ARCH'] == 'WINNT': - XPIDL_SOURCES += ['nsIWindowsShellService.idl'] -elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': - XPIDL_SOURCES += ['nsIMacShellService.idl'] -elif 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']: - XPIDL_SOURCES += ['nsIGNOMEShellService.idl'] - -XPIDL_MODULE = 'shellservice' - -if CONFIG['OS_ARCH'] == 'WINNT': - SOURCES += ['nsWindowsShellService.cpp'] -elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': - SOURCES += ['nsMacShellService.cpp'] -elif 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']: - SOURCES += ['nsGNOMEShellService.cpp'] - -if SOURCES: - FINAL_LIBRARY = 'browsercomps' - -EXTRA_COMPONENTS += [ - 'nsSetDefaultBrowser.js', - 'nsSetDefaultBrowser.manifest', -] - -EXTRA_JS_MODULES += ['ShellService.jsm'] - -for var in ('MOZ_APP_NAME', 'MOZ_APP_VERSION'): - DEFINES[var] = '"%s"' % CONFIG[var] - -CXXFLAGS += CONFIG['TK_CFLAGS'] diff --git a/components/shell/nsGNOMEShellService.cpp b/components/shell/nsGNOMEShellService.cpp deleted file mode 100644 index 9bc5f59..0000000 --- a/components/shell/nsGNOMEShellService.cpp +++ /dev/null @@ -1,637 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "mozilla/ArrayUtils.h" - -#include "nsCOMPtr.h" -#include "nsGNOMEShellService.h" -#include "nsShellService.h" -#include "nsIServiceManager.h" -#include "nsIFile.h" -#include "nsIProperties.h" -#include "nsDirectoryServiceDefs.h" -#include "nsIPrefService.h" -#include "prenv.h" -#include "nsStringAPI.h" -#include "nsIGConfService.h" -#include "nsIGIOService.h" -#include "nsIGSettingsService.h" -#include "nsIStringBundle.h" -#include "nsIOutputStream.h" -#include "nsIProcess.h" -#include "nsServiceManagerUtils.h" -#include "nsComponentManagerUtils.h" -#include "nsIDOMHTMLImageElement.h" -#include "nsIImageLoadingContent.h" -#include "imgIRequest.h" -#include "imgIContainer.h" -#include "mozilla/Sprintf.h" -#if defined(MOZ_WIDGET_GTK) -#include "nsIImageToPixbuf.h" -#endif -#include "nsXULAppAPI.h" - -#include <glib.h> -#include <glib-object.h> -#include <gtk/gtk.h> -#include <gdk/gdk.h> -#include <gdk-pixbuf/gdk-pixbuf.h> -#include <limits.h> -#include <stdlib.h> - -using namespace mozilla; - -struct ProtocolAssociation -{ - const char *name; - bool essential; -}; - -struct MimeTypeAssociation -{ - const char *mimeType; - const char *extensions; -}; - -static const ProtocolAssociation appProtocols[] = { - { "http", true }, - { "https", true }, - { "ftp", false }, - { "chrome", false } -}; - -static const MimeTypeAssociation appTypes[] = { - { "text/html", "htm html shtml" }, - { "application/xhtml+xml", "xhtml xht" } -}; - -// GConf registry key constants -#define DG_BACKGROUND "/desktop/gnome/background" - -static const char kDesktopImageKey[] = DG_BACKGROUND "/picture_filename"; -static const char kDesktopOptionsKey[] = DG_BACKGROUND "/picture_options"; -static const char kDesktopDrawBGKey[] = DG_BACKGROUND "/draw_background"; -static const char kDesktopColorKey[] = DG_BACKGROUND "/primary_color"; - -static const char kDesktopBGSchema[] = "org.gnome.desktop.background"; -static const char kDesktopImageGSKey[] = "picture-uri"; -static const char kDesktopOptionGSKey[] = "picture-options"; -static const char kDesktopDrawBGGSKey[] = "draw-background"; -static const char kDesktopColorGSKey[] = "primary-color"; - -nsresult -nsGNOMEShellService::Init() -{ - nsresult rv; - - // GConf, GSettings or GIO _must_ be available, or we do not allow - // CreateInstance to succeed. - - nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID); - nsCOMPtr<nsIGIOService> giovfs = - do_GetService(NS_GIOSERVICE_CONTRACTID); - nsCOMPtr<nsIGSettingsService> gsettings = - do_GetService(NS_GSETTINGSSERVICE_CONTRACTID); - - if (!gconf && !giovfs && !gsettings) - return NS_ERROR_NOT_AVAILABLE; - - // Check G_BROKEN_FILENAMES. If it's set, then filenames in glib use - // the locale encoding. If it's not set, they use UTF-8. - mUseLocaleFilenames = PR_GetEnv("G_BROKEN_FILENAMES") != nullptr; - - if (GetAppPathFromLauncher()) - return NS_OK; - - nsCOMPtr<nsIProperties> dirSvc - (do_GetService("@mozilla.org/file/directory_service;1")); - NS_ENSURE_TRUE(dirSvc, NS_ERROR_NOT_AVAILABLE); - - nsCOMPtr<nsIFile> appPath; - rv = dirSvc->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile), - getter_AddRefs(appPath)); - NS_ENSURE_SUCCESS(rv, rv); - - return appPath->GetNativePath(mAppPath); -} - -NS_IMPL_ISUPPORTS(nsGNOMEShellService, nsIGNOMEShellService, nsIShellService) - -bool -nsGNOMEShellService::GetAppPathFromLauncher() -{ - gchar *tmp; - - const char *launcher = PR_GetEnv("MOZ_APP_LAUNCHER"); - if (!launcher) - return false; - - if (g_path_is_absolute(launcher)) { - mAppPath = launcher; - tmp = g_path_get_basename(launcher); - gchar *fullpath = g_find_program_in_path(tmp); - if (fullpath && mAppPath.Equals(fullpath)) - mAppIsInPath = true; - g_free(fullpath); - } else { - tmp = g_find_program_in_path(launcher); - if (!tmp) - return false; - mAppPath = tmp; - mAppIsInPath = true; - } - - g_free(tmp); - return true; -} - -bool -nsGNOMEShellService::KeyMatchesAppName(const char *aKeyValue) const -{ - - gchar *commandPath; - if (mUseLocaleFilenames) { - gchar *nativePath = g_filename_from_utf8(aKeyValue, -1, - nullptr, nullptr, nullptr); - if (!nativePath) { - NS_ERROR("Error converting path to filesystem encoding"); - return false; - } - - commandPath = g_find_program_in_path(nativePath); - g_free(nativePath); - } else { - commandPath = g_find_program_in_path(aKeyValue); - } - - if (!commandPath) - return false; - - bool matches = mAppPath.Equals(commandPath); - g_free(commandPath); - return matches; -} - -bool -nsGNOMEShellService::CheckHandlerMatchesAppName(const nsACString &handler) const -{ - gint argc; - gchar **argv; - nsAutoCString command(handler); - - // The string will be something of the form: [/path/to/]browser "%s" - // We want to remove all of the parameters and get just the binary name. - - if (g_shell_parse_argv(command.get(), &argc, &argv, nullptr) && argc > 0) { - command.Assign(argv[0]); - g_strfreev(argv); - } - - if (!KeyMatchesAppName(command.get())) - return false; // the handler is set to another app - - return true; -} - -NS_IMETHODIMP -nsGNOMEShellService::IsDefaultBrowser(bool aStartupCheck, - bool aForAllTypes, - bool* aIsDefaultBrowser) -{ - *aIsDefaultBrowser = false; - - nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID); - nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID); - - bool enabled; - nsAutoCString handler; - nsCOMPtr<nsIGIOMimeApp> gioApp; - - for (unsigned int i = 0; i < ArrayLength(appProtocols); ++i) { - if (!appProtocols[i].essential) - continue; - - if (gconf) { - handler.Truncate(); - gconf->GetAppForProtocol(nsDependentCString(appProtocols[i].name), - &enabled, handler); - - if (!CheckHandlerMatchesAppName(handler) || !enabled) - return NS_OK; // the handler is disabled or set to another app - } - - if (giovfs) { - handler.Truncate(); - giovfs->GetAppForURIScheme(nsDependentCString(appProtocols[i].name), - getter_AddRefs(gioApp)); - if (!gioApp) - return NS_OK; - - gioApp->GetCommand(handler); - - if (!CheckHandlerMatchesAppName(handler)) - return NS_OK; // the handler is set to another app - } - } - - *aIsDefaultBrowser = true; - - return NS_OK; -} - -NS_IMETHODIMP -nsGNOMEShellService::SetDefaultBrowser(bool aClaimAllTypes, - bool aForAllUsers) -{ -#ifdef DEBUG - if (aForAllUsers) - NS_WARNING("Setting the default browser for all users is not yet supported"); -#endif - - nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID); - nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID); - if (gconf) { - nsAutoCString appKeyValue; - if (mAppIsInPath) { - // mAppPath is in the users path, so use only the basename as the launcher - gchar *tmp = g_path_get_basename(mAppPath.get()); - appKeyValue = tmp; - g_free(tmp); - } else { - appKeyValue = mAppPath; - } - - appKeyValue.AppendLiteral(" %s"); - - for (unsigned int i = 0; i < ArrayLength(appProtocols); ++i) { - if (appProtocols[i].essential || aClaimAllTypes) { - gconf->SetAppForProtocol(nsDependentCString(appProtocols[i].name), - appKeyValue); - } - } - } - - if (giovfs) { - nsresult rv; - nsCOMPtr<nsIStringBundleService> bundleService = - do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr<nsIStringBundle> brandBundle; - rv = bundleService->CreateBundle(BRAND_PROPERTIES, getter_AddRefs(brandBundle)); - NS_ENSURE_SUCCESS(rv, rv); - - nsString brandShortName; - brandBundle->GetStringFromName(u"brandShortName", - getter_Copies(brandShortName)); - - // use brandShortName as the application id. - NS_ConvertUTF16toUTF8 id(brandShortName); - nsCOMPtr<nsIGIOMimeApp> appInfo; - rv = giovfs->CreateAppFromCommand(mAppPath, - id, - getter_AddRefs(appInfo)); - NS_ENSURE_SUCCESS(rv, rv); - - // set handler for the protocols - for (unsigned int i = 0; i < ArrayLength(appProtocols); ++i) { - if (appProtocols[i].essential || aClaimAllTypes) { - appInfo->SetAsDefaultForURIScheme(nsDependentCString(appProtocols[i].name)); - } - } - - // set handler for .html and xhtml files and MIME types: - if (aClaimAllTypes) { - // Add mime types for html, xhtml extension and set app to just created appinfo. - for (unsigned int i = 0; i < ArrayLength(appTypes); ++i) { - appInfo->SetAsDefaultForMimeType(nsDependentCString(appTypes[i].mimeType)); - appInfo->SetAsDefaultForFileExtensions(nsDependentCString(appTypes[i].extensions)); - } - } - } - - nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID)); - if (prefs) { - (void) prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, true); - // Reset the number of times the dialog should be shown - // before it is silenced. - (void) prefs->SetIntPref(PREF_DEFAULTBROWSERCHECKCOUNT, 0); - } - - return NS_OK; -} - -NS_IMETHODIMP -nsGNOMEShellService::GetCanSetDesktopBackground(bool* aResult) -{ - // setting desktop background is currently only supported - // for Gnome or desktops using the same GSettings and GConf keys - const char* gnomeSession = getenv("GNOME_DESKTOP_SESSION_ID"); - if (gnomeSession) { - *aResult = true; - } else { - *aResult = false; - } - - return NS_OK; -} - -static nsresult -WriteImage(const nsCString& aPath, imgIContainer* aImage) -{ -#if !defined(MOZ_WIDGET_GTK) - return NS_ERROR_NOT_AVAILABLE; -#else - nsCOMPtr<nsIImageToPixbuf> imgToPixbuf = - do_GetService("@mozilla.org/widget/image-to-gdk-pixbuf;1"); - if (!imgToPixbuf) - return NS_ERROR_NOT_AVAILABLE; - - GdkPixbuf* pixbuf = imgToPixbuf->ConvertImageToPixbuf(aImage); - if (!pixbuf) - return NS_ERROR_NOT_AVAILABLE; - - gboolean res = gdk_pixbuf_save(pixbuf, aPath.get(), "png", nullptr, nullptr); - - g_object_unref(pixbuf); - return res ? NS_OK : NS_ERROR_FAILURE; -#endif -} - -NS_IMETHODIMP -nsGNOMEShellService::SetDesktopBackground(nsIDOMElement* aElement, - int32_t aPosition) -{ - nsresult rv; - nsCOMPtr<nsIImageLoadingContent> imageContent = do_QueryInterface(aElement, &rv); - if (!imageContent) return rv; - - // get the image container - nsCOMPtr<imgIRequest> request; - rv = imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, - getter_AddRefs(request)); - if (!request) return rv; - nsCOMPtr<imgIContainer> container; - rv = request->GetImage(getter_AddRefs(container)); - if (!container) return rv; - - // Set desktop wallpaper filling style - nsAutoCString options; - if (aPosition == BACKGROUND_TILE) - options.AssignLiteral("wallpaper"); - else if (aPosition == BACKGROUND_STRETCH) - options.AssignLiteral("stretched"); - else if (aPosition == BACKGROUND_FILL) - options.AssignLiteral("zoom"); - else if (aPosition == BACKGROUND_FIT) - options.AssignLiteral("scaled"); - else - options.AssignLiteral("centered"); - - // Write the background file to the home directory. - nsAutoCString filePath(PR_GetEnv("HOME")); - - // get the product brand name from localized strings - nsString brandName; - nsCID bundleCID = NS_STRINGBUNDLESERVICE_CID; - nsCOMPtr<nsIStringBundleService> bundleService(do_GetService(bundleCID)); - if (bundleService) { - nsCOMPtr<nsIStringBundle> brandBundle; - rv = bundleService->CreateBundle(BRAND_PROPERTIES, - getter_AddRefs(brandBundle)); - if (NS_SUCCEEDED(rv) && brandBundle) { - rv = brandBundle->GetStringFromName(u"brandShortName", - getter_Copies(brandName)); - NS_ENSURE_SUCCESS(rv, rv); - } - } - - // build the file name - filePath.Append('/'); - filePath.Append(NS_ConvertUTF16toUTF8(brandName)); - filePath.AppendLiteral("_wallpaper.png"); - - // write the image to a file in the home dir - rv = WriteImage(filePath, container); - NS_ENSURE_SUCCESS(rv, rv); - - // Try GSettings first. If we don't have GSettings or the right schema, fall back - // to using GConf instead. Note that if GSettings works ok, the changes get - // mirrored to GConf by the gsettings->gconf bridge in gnome-settings-daemon - nsCOMPtr<nsIGSettingsService> gsettings = - do_GetService(NS_GSETTINGSSERVICE_CONTRACTID); - if (gsettings) { - nsCOMPtr<nsIGSettingsCollection> background_settings; - gsettings->GetCollectionForSchema( - NS_LITERAL_CSTRING(kDesktopBGSchema), getter_AddRefs(background_settings)); - if (background_settings) { - gchar *file_uri = g_filename_to_uri(filePath.get(), nullptr, nullptr); - if (!file_uri) - return NS_ERROR_FAILURE; - - background_settings->SetString(NS_LITERAL_CSTRING(kDesktopOptionGSKey), - options); - - background_settings->SetString(NS_LITERAL_CSTRING(kDesktopImageGSKey), - nsDependentCString(file_uri)); - g_free(file_uri); - background_settings->SetBoolean(NS_LITERAL_CSTRING(kDesktopDrawBGGSKey), - true); - return rv; - } - } - - // if the file was written successfully, set it as the system wallpaper - nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID); - - if (gconf) { - gconf->SetString(NS_LITERAL_CSTRING(kDesktopOptionsKey), options); - - // Set the image to an empty string first to force a refresh - // (since we could be writing a new image on top of an existing - // PaleMoon_wallpaper.png and nautilus doesn't monitor the file for changes) - gconf->SetString(NS_LITERAL_CSTRING(kDesktopImageKey), - EmptyCString()); - - gconf->SetString(NS_LITERAL_CSTRING(kDesktopImageKey), filePath); - gconf->SetBool(NS_LITERAL_CSTRING(kDesktopDrawBGKey), true); - } - - return rv; -} - -#define COLOR_16_TO_8_BIT(_c) ((_c) >> 8) -#define COLOR_8_TO_16_BIT(_c) ((_c) << 8 | (_c)) - -NS_IMETHODIMP -nsGNOMEShellService::GetDesktopBackgroundColor(uint32_t *aColor) -{ - nsCOMPtr<nsIGSettingsService> gsettings = - do_GetService(NS_GSETTINGSSERVICE_CONTRACTID); - nsCOMPtr<nsIGSettingsCollection> background_settings; - nsAutoCString background; - - if (gsettings) { - gsettings->GetCollectionForSchema( - NS_LITERAL_CSTRING(kDesktopBGSchema), getter_AddRefs(background_settings)); - if (background_settings) { - background_settings->GetString(NS_LITERAL_CSTRING(kDesktopColorGSKey), - background); - } - } - - if (!background_settings) { - nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID); - if (gconf) - gconf->GetString(NS_LITERAL_CSTRING(kDesktopColorKey), background); - } - - if (background.IsEmpty()) { - *aColor = 0; - return NS_OK; - } - - GdkColor color; - gboolean success = gdk_color_parse(background.get(), &color); - - NS_ENSURE_TRUE(success, NS_ERROR_FAILURE); - - *aColor = COLOR_16_TO_8_BIT(color.red) << 16 | - COLOR_16_TO_8_BIT(color.green) << 8 | - COLOR_16_TO_8_BIT(color.blue); - return NS_OK; -} - -static void -ColorToCString(uint32_t aColor, nsCString& aResult) -{ - // The #rrrrggggbbbb format is used to match gdk_color_to_string() - char *buf = aResult.BeginWriting(13); - if (!buf) - return; - - uint16_t red = COLOR_8_TO_16_BIT((aColor >> 16) & 0xff); - uint16_t green = COLOR_8_TO_16_BIT((aColor >> 8) & 0xff); - uint16_t blue = COLOR_8_TO_16_BIT(aColor & 0xff); - - snprintf(buf, 14, "#%04x%04x%04x", red, green, blue); -} - -NS_IMETHODIMP -nsGNOMEShellService::SetDesktopBackgroundColor(uint32_t aColor) -{ - NS_ASSERTION(aColor <= 0xffffff, "aColor has extra bits"); - nsAutoCString colorString; - ColorToCString(aColor, colorString); - - nsCOMPtr<nsIGSettingsService> gsettings = - do_GetService(NS_GSETTINGSSERVICE_CONTRACTID); - if (gsettings) { - nsCOMPtr<nsIGSettingsCollection> background_settings; - gsettings->GetCollectionForSchema( - NS_LITERAL_CSTRING(kDesktopBGSchema), getter_AddRefs(background_settings)); - if (background_settings) { - background_settings->SetString(NS_LITERAL_CSTRING(kDesktopColorGSKey), - colorString); - return NS_OK; - } - } - - nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID); - - if (gconf) { - gconf->SetString(NS_LITERAL_CSTRING(kDesktopColorKey), colorString); - } - - return NS_OK; -} - -NS_IMETHODIMP -nsGNOMEShellService::OpenApplication(int32_t aApplication) -{ - nsAutoCString scheme; - if (aApplication == APPLICATION_MAIL) - scheme.AssignLiteral("mailto"); - else if (aApplication == APPLICATION_NEWS) - scheme.AssignLiteral("news"); - else - return NS_ERROR_NOT_AVAILABLE; - - nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID); - if (giovfs) { - nsCOMPtr<nsIGIOMimeApp> gioApp; - giovfs->GetAppForURIScheme(scheme, getter_AddRefs(gioApp)); - if (gioApp) - return gioApp->Launch(EmptyCString()); - } - - nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID); - if (!gconf) - return NS_ERROR_FAILURE; - - bool enabled; - nsAutoCString appCommand; - gconf->GetAppForProtocol(scheme, &enabled, appCommand); - - if (!enabled) - return NS_ERROR_FAILURE; - - // XXX we don't currently handle launching a terminal window. - // If the handler requires a terminal, bail. - bool requiresTerminal; - gconf->HandlerRequiresTerminal(scheme, &requiresTerminal); - if (requiresTerminal) - return NS_ERROR_FAILURE; - - // Perform shell argument expansion - int argc; - char **argv; - if (!g_shell_parse_argv(appCommand.get(), &argc, &argv, nullptr)) - return NS_ERROR_FAILURE; - - char **newArgv = new char*[argc + 1]; - int newArgc = 0; - - // Run through the list of arguments. Copy all of them to the new - // argv except for %s, which we skip. - for (int i = 0; i < argc; ++i) { - if (strcmp(argv[i], "%s") != 0) - newArgv[newArgc++] = argv[i]; - } - - newArgv[newArgc] = nullptr; - - gboolean err = g_spawn_async(nullptr, newArgv, nullptr, G_SPAWN_SEARCH_PATH, - nullptr, nullptr, nullptr, nullptr); - - g_strfreev(argv); - delete[] newArgv; - - return err ? NS_OK : NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -nsGNOMEShellService::OpenApplicationWithURI(nsIFile* aApplication, const nsACString& aURI) -{ - nsresult rv; - nsCOMPtr<nsIProcess> process = - do_CreateInstance("@mozilla.org/process/util;1", &rv); - if (NS_FAILED(rv)) - return rv; - - rv = process->Init(aApplication); - if (NS_FAILED(rv)) - return rv; - - const nsCString spec(aURI); - const char* specStr = spec.get(); - return process->Run(false, &specStr, 1); -} - -NS_IMETHODIMP -nsGNOMEShellService::GetDefaultFeedReader(nsIFile** _retval) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} diff --git a/components/shell/nsGNOMEShellService.h b/components/shell/nsGNOMEShellService.h deleted file mode 100644 index a7b0038..0000000 --- a/components/shell/nsGNOMEShellService.h +++ /dev/null @@ -1,36 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef nsgnomeshellservice_h____ -#define nsgnomeshellservice_h____ - -#include "nsIGNOMEShellService.h" -#include "nsStringAPI.h" -#include "mozilla/Attributes.h" - -class nsGNOMEShellService final : public nsIGNOMEShellService -{ -public: - nsGNOMEShellService() : mAppIsInPath(false) { } - - NS_DECL_ISUPPORTS - NS_DECL_NSISHELLSERVICE - NS_DECL_NSIGNOMESHELLSERVICE - - nsresult Init(); - -private: - ~nsGNOMEShellService() {} - - bool KeyMatchesAppName(const char *aKeyValue) const; - bool CheckHandlerMatchesAppName(const nsACString& handler) const; - - bool GetAppPathFromLauncher(); - bool mUseLocaleFilenames; - nsCString mAppPath; - bool mAppIsInPath; -}; - -#endif // nsgnomeshellservice_h____ diff --git a/components/shell/nsIGNOMEShellService.idl b/components/shell/nsIGNOMEShellService.idl deleted file mode 100644 index 842ce5e..0000000 --- a/components/shell/nsIGNOMEShellService.idl +++ /dev/null @@ -1,19 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsIShellService.idl" - -[scriptable, uuid(2ce5c803-edcd-443d-98eb-ceba86d02d13)] -interface nsIGNOMEShellService : nsIShellService -{ - /** - * Used to determine whether or not to offer "Set as desktop background" - * functionality. Even if shell service is available it is not - * guaranteed that it is able to set the background for every desktop - * which is especially true for Linux with its many different desktop - * environments. - */ - readonly attribute boolean canSetDesktopBackground; -}; - diff --git a/components/shell/nsIMacShellService.idl b/components/shell/nsIMacShellService.idl deleted file mode 100644 index 6a532bb..0000000 --- a/components/shell/nsIMacShellService.idl +++ /dev/null @@ -1,15 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsIShellService.idl" - -[scriptable, uuid(387fdc80-0077-4b60-a0d9-d9e80a83ba64)] -interface nsIMacShellService : nsIShellService -{ - const long APPLICATION_KEYCHAIN_ACCESS = 2; - const long APPLICATION_NETWORK = 3; - const long APPLICATION_DESKTOP = 4; -}; - diff --git a/components/shell/nsIShellService.idl b/components/shell/nsIShellService.idl deleted file mode 100644 index 3e7e94b..0000000 --- a/components/shell/nsIShellService.idl +++ /dev/null @@ -1,95 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" - -interface nsIDOMElement; -interface nsIFile; - -[scriptable, uuid(2d1a95e4-5bd8-4eeb-b0a8-c1455fd2a357)] -interface nsIShellService : nsISupports -{ - /** - * Determines whether or not Firefox is the "Default Browser." - * This is simply whether or not Firefox is registered to handle - * http links. - * - * @param aStartupCheck true if this is the check being performed - * by the first browser window at startup, - * false otherwise. - * @param aForAllTypes true if the check should be made for HTTP and HTML. - * false if the check should be made for HTTP only. - * This parameter may be ignored on some platforms. - */ - boolean isDefaultBrowser(in boolean aStartupCheck, - [optional] in boolean aForAllTypes); - - /** - * Registers Firefox as the "Default Browser." - * - * @param aClaimAllTypes Register Firefox as the handler for - * additional protocols (ftp, chrome etc) - * and web documents (.html, .xhtml etc). - * @param aForAllUsers Whether or not Firefox should attempt - * to become the default browser for all - * users on a multi-user system. - */ - void setDefaultBrowser(in boolean aClaimAllTypes, in boolean aForAllUsers); - - /** - * Flags for positioning/sizing of the Desktop Background image. - */ - const long BACKGROUND_TILE = 1; - const long BACKGROUND_STRETCH = 2; - const long BACKGROUND_CENTER = 3; - const long BACKGROUND_FILL = 4; - const long BACKGROUND_FIT = 5; - - /** - * Sets the desktop background image using either the HTML <IMG> - * element supplied or the background image of the element supplied. - * - * @param aImageElement Either a HTML <IMG> element or an element with - * a background image from which to source the - * background image. - * @param aPosition How to place the image on the desktop - */ - void setDesktopBackground(in nsIDOMElement aElement, in long aPosition); - - /** - * Constants identifying applications that can be opened with - * openApplication. - */ - const long APPLICATION_MAIL = 0; - const long APPLICATION_NEWS = 1; - - /** - * Opens the application specified. If more than one application of the - * given type is available on the system, the default or "preferred" - * application is used. - */ - void openApplication(in long aApplication); - - /** - * The desktop background color, visible when no background image is - * used, or if the background image is centered and does not fill the - * entire screen. A rgb value, where (r << 16 | g << 8 | b) - */ - attribute unsigned long desktopBackgroundColor; - - /** - * Opens an application with a specific URI to load. - * @param application - * The application file (or bundle directory, on OS X) - * @param uri - * The uri to be loaded by the application - */ - void openApplicationWithURI(in nsIFile aApplication, in ACString aURI); - - /** - * The default system handler for web feeds - */ - readonly attribute nsIFile defaultFeedReader; -}; diff --git a/components/shell/nsIWindowsShellService.idl b/components/shell/nsIWindowsShellService.idl deleted file mode 100644 index 57ed370..0000000 --- a/components/shell/nsIWindowsShellService.idl +++ /dev/null @@ -1,17 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsIShellService.idl" - -[scriptable, uuid(f8a26b94-49e5-4441-8fbc-315e0b4f22ef)] -interface nsIWindowsShellService : nsIShellService -{ - /** - * Provides the shell service an opportunity to do some Win7+ shortcut - * maintenance needed on initial startup of the browser. - */ - void shortcutMaintenance(); -}; - diff --git a/components/shell/nsMacShellService.cpp b/components/shell/nsMacShellService.cpp deleted file mode 100644 index d8d6403..0000000 --- a/components/shell/nsMacShellService.cpp +++ /dev/null @@ -1,434 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsDirectoryServiceDefs.h" -#include "nsIDOMElement.h" -#include "nsIDOMHTMLImageElement.h" -#include "nsIImageLoadingContent.h" -#include "nsIDocument.h" -#include "nsIContent.h" -#include "nsILocalFileMac.h" -#include "nsIObserverService.h" -#include "nsIPrefService.h" -#include "nsIServiceManager.h" -#include "nsIStringBundle.h" -#include "nsIURL.h" -#include "nsIWebBrowserPersist.h" -#include "nsMacShellService.h" -#include "nsIProperties.h" -#include "nsServiceManagerUtils.h" -#include "nsShellService.h" -#include "nsStringAPI.h" -#include "nsIDocShell.h" -#include "nsILoadContext.h" - -#include <CoreFoundation/CoreFoundation.h> -#include <ApplicationServices/ApplicationServices.h> - -#define NETWORK_PREFPANE NS_LITERAL_CSTRING("/System/Library/PreferencePanes/Network.prefPane") -#define DESKTOP_PREFPANE NS_LITERAL_CSTRING("/System/Library/PreferencePanes/DesktopScreenEffectsPref.prefPane") - -#define SAFARI_BUNDLE_IDENTIFIER "com.apple.Safari" - -NS_IMPL_ISUPPORTS(nsMacShellService, nsIMacShellService, nsIShellService, nsIWebProgressListener) - -NS_IMETHODIMP -nsMacShellService::IsDefaultBrowser(bool aStartupCheck, - bool aForAllTypes, - bool* aIsDefaultBrowser) -{ - *aIsDefaultBrowser = false; - - CFStringRef firefoxID = ::CFBundleGetIdentifier(::CFBundleGetMainBundle()); - if (!firefoxID) { - // CFBundleGetIdentifier is expected to return nullptr only if the specified - // bundle doesn't have a bundle identifier in its plist. In this case, that - // means a failure, since our bundle does have an identifier. - return NS_ERROR_FAILURE; - } - - // Get the default http handler's bundle ID (or nullptr if it has not been - // explicitly set) - CFStringRef defaultBrowserID = ::LSCopyDefaultHandlerForURLScheme(CFSTR("http")); - if (defaultBrowserID) { - *aIsDefaultBrowser = ::CFStringCompare(firefoxID, defaultBrowserID, 0) == kCFCompareEqualTo; - ::CFRelease(defaultBrowserID); - } - - return NS_OK; -} - -NS_IMETHODIMP -nsMacShellService::SetDefaultBrowser(bool aClaimAllTypes, bool aForAllUsers) -{ - // Note: We don't support aForAllUsers on Mac OS X. - - CFStringRef firefoxID = ::CFBundleGetIdentifier(::CFBundleGetMainBundle()); - if (!firefoxID) { - return NS_ERROR_FAILURE; - } - - if (::LSSetDefaultHandlerForURLScheme(CFSTR("http"), firefoxID) != noErr) { - return NS_ERROR_FAILURE; - } - if (::LSSetDefaultHandlerForURLScheme(CFSTR("https"), firefoxID) != noErr) { - return NS_ERROR_FAILURE; - } - - if (aClaimAllTypes) { - if (::LSSetDefaultHandlerForURLScheme(CFSTR("ftp"), firefoxID) != noErr) { - return NS_ERROR_FAILURE; - } - if (::LSSetDefaultRoleHandlerForContentType(kUTTypeHTML, kLSRolesAll, firefoxID) != noErr) { - return NS_ERROR_FAILURE; - } - } - - nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID)); - if (prefs) { - (void) prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, true); - // Reset the number of times the dialog should be shown - // before it is silenced. - (void) prefs->SetIntPref(PREF_DEFAULTBROWSERCHECKCOUNT, 0); - } - - return NS_OK; -} - -NS_IMETHODIMP -nsMacShellService::SetDesktopBackground(nsIDOMElement* aElement, - int32_t aPosition) -{ - // Note: We don't support aPosition on OS X. - - // Get the image URI: - nsresult rv; - nsCOMPtr<nsIImageLoadingContent> imageContent = do_QueryInterface(aElement, - &rv); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr<nsIURI> imageURI; - rv = imageContent->GetCurrentURI(getter_AddRefs(imageURI)); - NS_ENSURE_SUCCESS(rv, rv); - - // We need the referer URI for nsIWebBrowserPersist::saveURI - nsCOMPtr<nsIContent> content = do_QueryInterface(aElement, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - nsIURI *docURI = content->OwnerDoc()->GetDocumentURI(); - if (!docURI) - return NS_ERROR_FAILURE; - - // Get the desired image file name - nsCOMPtr<nsIURL> imageURL(do_QueryInterface(imageURI)); - if (!imageURL) { - // XXXmano (bug 300293): Non-URL images (e.g. the data: protocol) are not - // yet supported. What filename should we take here? - return NS_ERROR_NOT_IMPLEMENTED; - } - - nsAutoCString fileName; - imageURL->GetFileName(fileName); - nsCOMPtr<nsIProperties> fileLocator - (do_GetService("@mozilla.org/file/directory_service;1", &rv)); - NS_ENSURE_SUCCESS(rv, rv); - - // Get the current user's "Pictures" folder (That's ~/Pictures): - fileLocator->Get(NS_OSX_PICTURE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), - getter_AddRefs(mBackgroundFile)); - if (!mBackgroundFile) - return NS_ERROR_OUT_OF_MEMORY; - - nsAutoString fileNameUnicode; - CopyUTF8toUTF16(fileName, fileNameUnicode); - - // and add the imgage file name itself: - mBackgroundFile->Append(fileNameUnicode); - - // Download the image; the desktop background will be set in OnStateChange() - nsCOMPtr<nsIWebBrowserPersist> wbp - (do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1", &rv)); - NS_ENSURE_SUCCESS(rv, rv); - - uint32_t flags = nsIWebBrowserPersist::PERSIST_FLAGS_NO_CONVERSION | - nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES | - nsIWebBrowserPersist::PERSIST_FLAGS_FROM_CACHE; - - wbp->SetPersistFlags(flags); - wbp->SetProgressListener(this); - - nsCOMPtr<nsILoadContext> loadContext; - nsCOMPtr<nsISupports> container = content->OwnerDoc()->GetContainer(); - nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container); - if (docShell) { - loadContext = do_QueryInterface(docShell); - } - - return wbp->SaveURI(imageURI, nullptr, - docURI, content->OwnerDoc()->GetReferrerPolicy(), - nullptr, nullptr, - mBackgroundFile, loadContext); -} - -NS_IMETHODIMP -nsMacShellService::OnProgressChange(nsIWebProgress* aWebProgress, - nsIRequest* aRequest, - int32_t aCurSelfProgress, - int32_t aMaxSelfProgress, - int32_t aCurTotalProgress, - int32_t aMaxTotalProgress) -{ - return NS_OK; -} - -NS_IMETHODIMP -nsMacShellService::OnLocationChange(nsIWebProgress* aWebProgress, - nsIRequest* aRequest, - nsIURI* aLocation, - uint32_t aFlags) -{ - return NS_OK; -} - -NS_IMETHODIMP -nsMacShellService::OnStatusChange(nsIWebProgress* aWebProgress, - nsIRequest* aRequest, - nsresult aStatus, - const char16_t* aMessage) -{ - return NS_OK; -} - -NS_IMETHODIMP -nsMacShellService::OnSecurityChange(nsIWebProgress* aWebProgress, - nsIRequest* aRequest, - uint32_t aState) -{ - return NS_OK; -} - -NS_IMETHODIMP -nsMacShellService::OnStateChange(nsIWebProgress* aWebProgress, - nsIRequest* aRequest, - uint32_t aStateFlags, - nsresult aStatus) -{ - if (aStateFlags & STATE_STOP) { - nsCOMPtr<nsIObserverService> os(do_GetService("@mozilla.org/observer-service;1")); - if (os) - os->NotifyObservers(nullptr, "shell:desktop-background-changed", nullptr); - - bool exists = false; - mBackgroundFile->Exists(&exists); - if (!exists) - return NS_OK; - - nsAutoCString nativePath; - mBackgroundFile->GetNativePath(nativePath); - - AEDesc tAEDesc = { typeNull, nil }; - OSErr err = noErr; - AliasHandle aliasHandle = nil; - FSRef pictureRef; - OSStatus status; - - // Convert the path into a FSRef - status = ::FSPathMakeRef((const UInt8*)nativePath.get(), &pictureRef, - nullptr); - if (status == noErr) { - err = ::FSNewAlias(nil, &pictureRef, &aliasHandle); - if (err == noErr && aliasHandle == nil) - err = paramErr; - - if (err == noErr) { - // We need the descriptor (based on the picture file reference) - // for the 'Set Desktop Picture' apple event. - char handleState = ::HGetState((Handle)aliasHandle); - ::HLock((Handle)aliasHandle); - err = ::AECreateDesc(typeAlias, *aliasHandle, - GetHandleSize((Handle)aliasHandle), &tAEDesc); - // unlock the alias handler - ::HSetState((Handle)aliasHandle, handleState); - ::DisposeHandle((Handle)aliasHandle); - } - if (err == noErr) { - AppleEvent tAppleEvent; - OSType sig = 'MACS'; - AEBuildError tAEBuildError; - // Create a 'Set Desktop Pictue' Apple Event - err = ::AEBuildAppleEvent(kAECoreSuite, kAESetData, typeApplSignature, - &sig, sizeof(OSType), kAutoGenerateReturnID, - kAnyTransactionID, &tAppleEvent, &tAEBuildError, - "'----':'obj '{want:type (prop),form:prop" \ - ",seld:type('dpic'),from:'null'()},data:(@)", - &tAEDesc); - if (err == noErr) { - AppleEvent reply = { typeNull, nil }; - // Sent the event we built, the reply event isn't necessary - err = ::AESend(&tAppleEvent, &reply, kAENoReply, kAENormalPriority, - kNoTimeOut, nil, nil); - ::AEDisposeDesc(&tAppleEvent); - } - } - } - } - - return NS_OK; -} - -NS_IMETHODIMP -nsMacShellService::OpenApplication(int32_t aApplication) -{ - nsresult rv = NS_OK; - CFURLRef appURL = nil; - OSStatus err = noErr; - - switch (aApplication) { - case nsIShellService::APPLICATION_MAIL: - { - CFURLRef tempURL = ::CFURLCreateWithString(kCFAllocatorDefault, - CFSTR("mailto:"), nullptr); - err = ::LSGetApplicationForURL(tempURL, kLSRolesAll, nullptr, &appURL); - ::CFRelease(tempURL); - } - break; - case nsIShellService::APPLICATION_NEWS: - { - CFURLRef tempURL = ::CFURLCreateWithString(kCFAllocatorDefault, - CFSTR("news:"), nullptr); - err = ::LSGetApplicationForURL(tempURL, kLSRolesAll, nullptr, &appURL); - ::CFRelease(tempURL); - } - break; - case nsIMacShellService::APPLICATION_KEYCHAIN_ACCESS: - err = ::LSGetApplicationForInfo('APPL', 'kcmr', nullptr, kLSRolesAll, - nullptr, &appURL); - break; - case nsIMacShellService::APPLICATION_NETWORK: - { - nsCOMPtr<nsIFile> lf; - rv = NS_NewNativeLocalFile(NETWORK_PREFPANE, true, getter_AddRefs(lf)); - NS_ENSURE_SUCCESS(rv, rv); - bool exists; - lf->Exists(&exists); - if (!exists) - return NS_ERROR_FILE_NOT_FOUND; - return lf->Launch(); - } - case nsIMacShellService::APPLICATION_DESKTOP: - { - nsCOMPtr<nsIFile> lf; - rv = NS_NewNativeLocalFile(DESKTOP_PREFPANE, true, getter_AddRefs(lf)); - NS_ENSURE_SUCCESS(rv, rv); - bool exists; - lf->Exists(&exists); - if (!exists) - return NS_ERROR_FILE_NOT_FOUND; - return lf->Launch(); - } - } - - if (appURL && err == noErr) { - err = ::LSOpenCFURLRef(appURL, nullptr); - rv = err != noErr ? NS_ERROR_FAILURE : NS_OK; - - ::CFRelease(appURL); - } - - return rv; -} - -NS_IMETHODIMP -nsMacShellService::GetDesktopBackgroundColor(uint32_t *aColor) -{ - // This method and |SetDesktopBackgroundColor| has no meaning on Mac OS X. - // The mac desktop preferences UI uses pictures for the few solid colors it - // supports. - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -nsMacShellService::SetDesktopBackgroundColor(uint32_t aColor) -{ - // This method and |GetDesktopBackgroundColor| has no meaning on Mac OS X. - // The mac desktop preferences UI uses pictures for the few solid colors it - // supports. - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -nsMacShellService::OpenApplicationWithURI(nsIFile* aApplication, const nsACString& aURI) -{ - nsCOMPtr<nsILocalFileMac> lfm(do_QueryInterface(aApplication)); - CFURLRef appURL; - nsresult rv = lfm->GetCFURL(&appURL); - if (NS_FAILED(rv)) - return rv; - - const nsCString spec(aURI); - const UInt8* uriString = (const UInt8*)spec.get(); - CFURLRef uri = ::CFURLCreateWithBytes(nullptr, uriString, aURI.Length(), - kCFStringEncodingUTF8, nullptr); - if (!uri) - return NS_ERROR_OUT_OF_MEMORY; - - CFArrayRef uris = ::CFArrayCreate(nullptr, (const void**)&uri, 1, nullptr); - if (!uris) { - ::CFRelease(uri); - return NS_ERROR_OUT_OF_MEMORY; - } - - LSLaunchURLSpec launchSpec; - launchSpec.appURL = appURL; - launchSpec.itemURLs = uris; - launchSpec.passThruParams = nullptr; - launchSpec.launchFlags = kLSLaunchDefaults; - launchSpec.asyncRefCon = nullptr; - - OSErr err = ::LSOpenFromURLSpec(&launchSpec, nullptr); - - ::CFRelease(uris); - ::CFRelease(uri); - - return err != noErr ? NS_ERROR_FAILURE : NS_OK; -} - -NS_IMETHODIMP -nsMacShellService::GetDefaultFeedReader(nsIFile** _retval) -{ - nsresult rv = NS_ERROR_FAILURE; - *_retval = nullptr; - - CFStringRef defaultHandlerID = ::LSCopyDefaultHandlerForURLScheme(CFSTR("feed")); - if (!defaultHandlerID) { - defaultHandlerID = ::CFStringCreateWithCString(kCFAllocatorDefault, - SAFARI_BUNDLE_IDENTIFIER, - kCFStringEncodingASCII); - } - - CFURLRef defaultHandlerURL = nullptr; - OSStatus status = ::LSFindApplicationForInfo(kLSUnknownCreator, - defaultHandlerID, - nullptr, // inName - nullptr, // outAppRef - &defaultHandlerURL); - - if (status == noErr && defaultHandlerURL) { - nsCOMPtr<nsILocalFileMac> defaultReader = - do_CreateInstance("@mozilla.org/file/local;1", &rv); - if (NS_SUCCEEDED(rv)) { - rv = defaultReader->InitWithCFURL(defaultHandlerURL); - if (NS_SUCCEEDED(rv)) { - NS_ADDREF(*_retval = defaultReader); - rv = NS_OK; - } - } - - ::CFRelease(defaultHandlerURL); - } - - ::CFRelease(defaultHandlerID); - - return rv; -} diff --git a/components/shell/nsMacShellService.h b/components/shell/nsMacShellService.h deleted file mode 100644 index db95278..0000000 --- a/components/shell/nsMacShellService.h +++ /dev/null @@ -1,32 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef nsmacshellservice_h____ -#define nsmacshellservice_h____ - -#include "nsIMacShellService.h" -#include "nsIWebProgressListener.h" -#include "nsIFile.h" -#include "nsCOMPtr.h" - -class nsMacShellService : public nsIMacShellService, - public nsIWebProgressListener -{ -public: - nsMacShellService() {}; - - NS_DECL_ISUPPORTS - NS_DECL_NSISHELLSERVICE - NS_DECL_NSIMACSHELLSERVICE - NS_DECL_NSIWEBPROGRESSLISTENER - -protected: - virtual ~nsMacShellService() {}; - -private: - nsCOMPtr<nsIFile> mBackgroundFile; -}; - -#endif // nsmacshellservice_h____ diff --git a/components/shell/nsSetDefaultBrowser.js b/components/shell/nsSetDefaultBrowser.js deleted file mode 100644 index c7a78c5..0000000 --- a/components/shell/nsSetDefaultBrowser.js +++ /dev/null @@ -1,30 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* - * --setDefaultBrowser commandline handler - * Makes the current executable the "default browser". - */ - -const Cc = Components.classes; -const Ci = Components.interfaces; -Components.utils.import("resource:///modules/ShellService.jsm"); -Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); - -function nsSetDefaultBrowser() {} - -nsSetDefaultBrowser.prototype = { - handle: function nsSetDefault_handle(aCmdline) { - if (aCmdline.handleFlag("setDefaultBrowser", false)) { - ShellService.setDefaultBrowser(true, true); - } - }, - - helpInfo: " --setDefaultBrowser Set this app as the default browser.\n", - - classID: Components.ID("{F57899D0-4E2C-4ac6-9E29-50C736103B0C}"), - QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]), -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([nsSetDefaultBrowser]); diff --git a/components/shell/nsSetDefaultBrowser.manifest b/components/shell/nsSetDefaultBrowser.manifest deleted file mode 100644 index bf3c0f0..0000000 --- a/components/shell/nsSetDefaultBrowser.manifest +++ /dev/null @@ -1,3 +0,0 @@ -component {F57899D0-4E2C-4ac6-9E29-50C736103B0C} nsSetDefaultBrowser.js -contract @mozilla.org/browser/default-browser-clh;1 {F57899D0-4E2C-4ac6-9E29-50C736103B0C} -category command-line-handler m-setdefaultbrowser @mozilla.org/browser/default-browser-clh;1 diff --git a/components/shell/nsShellService.h b/components/shell/nsShellService.h deleted file mode 100644 index 516a842..0000000 --- a/components/shell/nsShellService.h +++ /dev/null @@ -1,12 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#define PREF_CHECKDEFAULTBROWSER "browser.shell.checkDefaultBrowser" -#define PREF_SKIPDEFAULTBROWSERCHECK "browser.shell.skipDefaultBrowserCheck" -#define PREF_DEFAULTBROWSERCHECKCOUNT "browser.shell.defaultBrowserCheckCount" - -#define SHELLSERVICE_PROPERTIES "chrome://browser/locale/shellservice.properties" -#define BRAND_PROPERTIES "chrome://branding/locale/brand.properties" - diff --git a/components/shell/nsWindowsShellService.cpp b/components/shell/nsWindowsShellService.cpp deleted file mode 100644 index c4039b9..0000000 --- a/components/shell/nsWindowsShellService.cpp +++ /dev/null @@ -1,1277 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsWindowsShellService.h" - -#include "imgIContainer.h" -#include "imgIRequest.h" -#include "mozilla/gfx/2D.h" -#include "mozilla/RefPtr.h" -#include "nsIDOMElement.h" -#include "nsIDOMHTMLImageElement.h" -#include "nsIImageLoadingContent.h" -#include "nsIPrefService.h" -#include "nsIPrefLocalizedString.h" -#include "nsIServiceManager.h" -#include "nsIStringBundle.h" -#include "nsNetUtil.h" -#include "nsServiceManagerUtils.h" -#include "nsShellService.h" -#include "nsIProcess.h" -#include "nsICategoryManager.h" -#include "nsBrowserCompsCID.h" -#include "nsDirectoryServiceUtils.h" -#include "nsAppDirectoryServiceDefs.h" -#include "nsDirectoryServiceDefs.h" -#include "nsIWindowsRegKey.h" -#include "nsUnicharUtils.h" -#include "nsIWinTaskbar.h" -#include "nsISupportsPrimitives.h" -#include "nsIURLFormatter.h" -#include "nsThreadUtils.h" -#include "nsXULAppAPI.h" -#include "mozilla/WindowsVersion.h" - -#include "windows.h" -#include "shellapi.h" - -#ifdef _WIN32_WINNT -#undef _WIN32_WINNT -#endif -#define _WIN32_WINNT 0x0600 -#define INITGUID -#undef NTDDI_VERSION -#define NTDDI_VERSION NTDDI_WIN8 -// Needed for access to IApplicationActivationManager -#include <shlobj.h> - -#include <mbstring.h> -#include <shlwapi.h> - -#include <lm.h> -#undef ACCESS_READ - -#ifndef MAX_BUF -#define MAX_BUF 4096 -#endif - -#define REG_SUCCEEDED(val) \ - (val == ERROR_SUCCESS) - -#define REG_FAILED(val) \ - (val != ERROR_SUCCESS) - -#define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1" - -using mozilla::IsWin8OrLater; -using namespace mozilla; -using namespace mozilla::gfx; - -NS_IMPL_ISUPPORTS(nsWindowsShellService, nsIWindowsShellService, nsIShellService) - -static nsresult -OpenKeyForReading(HKEY aKeyRoot, const nsAString& aKeyName, HKEY* aKey) -{ - const nsString &flatName = PromiseFlatString(aKeyName); - - DWORD res = ::RegOpenKeyExW(aKeyRoot, flatName.get(), 0, KEY_READ, aKey); - switch (res) { - case ERROR_SUCCESS: - break; - case ERROR_ACCESS_DENIED: - return NS_ERROR_FILE_ACCESS_DENIED; - case ERROR_FILE_NOT_FOUND: - return NS_ERROR_NOT_AVAILABLE; - } - - return NS_OK; -} - -/////////////////////////////////////////////////////////////////////////////// -// Default Browser Registry Settings -// -// The setting of these values are made by an external binary since writing -// these values may require elevation. -// -// - File Extension Mappings -// ----------------------- -// The following file extensions: -// .htm .html .shtml .xht .xhtml -// are mapped like so: -// -// HKCU\SOFTWARE\Classes\.<ext>\ (default) REG_SZ PaleMoonHTML -// -// as aliases to the class: -// -// HKCU\SOFTWARE\Classes\PaleMoonHTML\ -// DefaultIcon (default) REG_SZ <apppath>,1 -// shell\open\command (default) REG_SZ <apppath> -osint -url "%1" -// shell\open\ddeexec (default) REG_SZ <empty string> -// -// - Windows Vista and above Protocol Handler -// -// HKCU\SOFTWARE\Classes\PaleMoonURL\ (default) REG_SZ <appname> URL -// EditFlags REG_DWORD 2 -// FriendlyTypeName REG_SZ <appname> URL -// DefaultIcon (default) REG_SZ <apppath>,1 -// shell\open\command (default) REG_SZ <apppath> -osint -url "%1" -// shell\open\ddeexec (default) REG_SZ <empty string> -// -// - Protocol Mappings -// ----------------- -// The following protocols: -// HTTP, HTTPS, FTP -// are mapped like so: -// -// HKCU\SOFTWARE\Classes\<protocol>\ -// DefaultIcon (default) REG_SZ <apppath>,1 -// shell\open\command (default) REG_SZ <apppath> -osint -url "%1" -// shell\open\ddeexec (default) REG_SZ <empty string> -// -// - Windows Start Menu (XP SP1 and newer) -// ------------------------------------------------- -// The following keys are set to make PaleMoon appear in the Start Menu as the -// browser: -// -// HKCU\SOFTWARE\Clients\StartMenuInternet\PaleMoon.EXE\ -// (default) REG_SZ <appname> -// DefaultIcon (default) REG_SZ <apppath>,0 -// InstallInfo HideIconsCommand REG_SZ <uninstpath> /HideShortcuts -// InstallInfo IconsVisible REG_DWORD 1 -// InstallInfo ReinstallCommand REG_SZ <uninstpath> /SetAsDefaultAppGlobal -// InstallInfo ShowIconsCommand REG_SZ <uninstpath> /ShowShortcuts -// shell\open\command (default) REG_SZ <apppath> -// shell\properties (default) REG_SZ <appname> &Options -// shell\properties\command (default) REG_SZ <apppath> -preferences -// shell\safemode (default) REG_SZ <appname> &Safe Mode -// shell\safemode\command (default) REG_SZ <apppath> -safe-mode -// - -// The values checked are all default values so the value name is not needed. -typedef struct { - const char* keyName; - const char* valueData; - const char* oldValueData; -} SETTING; - -#define APP_REG_NAME L"Pale Moon" -#define VAL_FILE_ICON "%APPPATH%,1" -#define VAL_OPEN "\"%APPPATH%\" -osint -url \"%1\"" -#define OLD_VAL_OPEN "\"%APPPATH%\" -requestPending -osint -url \"%1\"" -#define DI "\\DefaultIcon" -#define SOC "\\shell\\open\\command" -#define SOD "\\shell\\open\\ddeexec" -// Used for updating the FTP protocol handler's shell open command under HKCU. -#define FTP_SOC L"Software\\Classes\\ftp\\shell\\open\\command" - -#define MAKE_KEY_NAME1(PREFIX, MID) \ - PREFIX MID - -// The DefaultIcon registry key value should never be used when checking if -// PaleMoon is the default browser for file handlers since other applications -// (e.g. MS Office) may modify the DefaultIcon registry key value to add Icon -// Handlers. see http://msdn2.microsoft.com/en-us/library/aa969357.aspx for -// more info. The FTP protocol is not checked so advanced users can set the FTP -// handler to another application and still have PaleMoon check if it is the -// default HTTP and HTTPS handler. -// *** Do not add additional checks here unless you skip them when aForAllTypes -// is false below***. -static SETTING gSettings[] = { - // File Handler Class - // ***keep this as the first entry because when aForAllTypes is not set below - // it will skip over this check.*** - { MAKE_KEY_NAME1("PaleMoonHTML", SOC), VAL_OPEN, OLD_VAL_OPEN }, - - // Protocol Handler Class - for Vista and above - { MAKE_KEY_NAME1("PaleMoonURL", SOC), VAL_OPEN, OLD_VAL_OPEN }, - - // Protocol Handlers - { MAKE_KEY_NAME1("HTTP", DI), VAL_FILE_ICON }, - { MAKE_KEY_NAME1("HTTP", SOC), VAL_OPEN, OLD_VAL_OPEN }, - { MAKE_KEY_NAME1("HTTPS", DI), VAL_FILE_ICON }, - { MAKE_KEY_NAME1("HTTPS", SOC), VAL_OPEN, OLD_VAL_OPEN } -}; - -// The settings to disable DDE are separate from the default browser settings -// since they are only checked when PaleMoon is the default browser and if they -// are incorrect they are fixed without notifying the user. -static SETTING gDDESettings[] = { - // File Handler Class - { MAKE_KEY_NAME1("Software\\Classes\\PaleMoonHTML", SOD) }, - - // Protocol Handler Class - for Vista and above - { MAKE_KEY_NAME1("Software\\Classes\\PaleMoonURL", SOD) }, - - // Protocol Handlers - { MAKE_KEY_NAME1("Software\\Classes\\FTP", SOD) }, - { MAKE_KEY_NAME1("Software\\Classes\\HTTP", SOD) }, - { MAKE_KEY_NAME1("Software\\Classes\\HTTPS", SOD) } -}; - -nsresult -GetHelperPath(nsAutoString& aPath) -{ - nsresult rv; - nsCOMPtr<nsIProperties> directoryService = - do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr<nsIFile> appHelper; - rv = directoryService->Get(XRE_EXECUTABLE_FILE, - NS_GET_IID(nsIFile), - getter_AddRefs(appHelper)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = appHelper->SetNativeLeafName(NS_LITERAL_CSTRING("uninstall")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = appHelper->AppendNative(NS_LITERAL_CSTRING("helper.exe")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = appHelper->GetPath(aPath); - - aPath.Insert(L'"', 0); - aPath.Append(L'"'); - return rv; -} - -nsresult -LaunchHelper(nsAutoString& aPath) -{ - STARTUPINFOW si = {sizeof(si), 0}; - PROCESS_INFORMATION pi = {0}; - - if (!CreateProcessW(nullptr, (LPWSTR)aPath.get(), nullptr, nullptr, FALSE, - 0, nullptr, nullptr, &si, &pi)) { - return NS_ERROR_FAILURE; - } - - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - return NS_OK; -} - -NS_IMETHODIMP -nsWindowsShellService::ShortcutMaintenance() -{ - nsresult rv; - - // XXX App ids were updated to a constant install path hash, - // XXX this code can be removed after a few upgrade cycles. - - // Launch helper.exe so it can update the application user model ids on - // shortcuts in the user's taskbar and start menu. This keeps older pinned - // shortcuts grouped correctly after major updates. Note, we also do this - // through the upgrade installer script, however, this is the only place we - // have a chance to trap links created by users who do control the install/ - // update process of the browser. - - nsCOMPtr<nsIWinTaskbar> taskbarInfo = - do_GetService(NS_TASKBAR_CONTRACTID); - if (!taskbarInfo) // If we haven't built with win7 sdk features, this fails. - return NS_OK; - - // Avoid if this isn't Win7+ - bool isSupported = false; - taskbarInfo->GetAvailable(&isSupported); - if (!isSupported) - return NS_OK; - - nsAutoString appId; - if (NS_FAILED(taskbarInfo->GetDefaultGroupId(appId))) - return NS_ERROR_UNEXPECTED; - - NS_NAMED_LITERAL_CSTRING(prefName, "browser.taskbar.lastgroupid"); - nsCOMPtr<nsIPrefBranch> prefs = - do_GetService(NS_PREFSERVICE_CONTRACTID); - if (!prefs) - return NS_ERROR_UNEXPECTED; - - nsCOMPtr<nsISupportsString> prefString; - rv = prefs->GetComplexValue(prefName.get(), - NS_GET_IID(nsISupportsString), - getter_AddRefs(prefString)); - if (NS_SUCCEEDED(rv)) { - nsAutoString version; - prefString->GetData(version); - if (!version.IsEmpty() && version.Equals(appId)) { - // We're all good, get out of here. - return NS_OK; - } - } - // Update the version in prefs - prefString = - do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv); - if (NS_FAILED(rv)) - return rv; - - prefString->SetData(appId); - rv = prefs->SetComplexValue(prefName.get(), - NS_GET_IID(nsISupportsString), - prefString); - if (NS_FAILED(rv)) { - NS_WARNING("Couldn't set last user model id!"); - return NS_ERROR_UNEXPECTED; - } - - nsAutoString appHelperPath; - if (NS_FAILED(GetHelperPath(appHelperPath))) - return NS_ERROR_UNEXPECTED; - - appHelperPath.AppendLiteral(" /UpdateShortcutAppUserModelIds"); - - return LaunchHelper(appHelperPath); -} - -static bool -IsAARDefault(const RefPtr<IApplicationAssociationRegistration>& pAAR, - LPCWSTR aClassName) -{ - // Make sure the Prog ID matches what we have - LPWSTR registeredApp; - bool isProtocol = *aClassName != L'.'; - ASSOCIATIONTYPE queryType = isProtocol ? AT_URLPROTOCOL : AT_FILEEXTENSION; - HRESULT hr = pAAR->QueryCurrentDefault(aClassName, queryType, AL_EFFECTIVE, - ®isteredApp); - if (FAILED(hr)) { - return false; - } - - LPCWSTR progID = isProtocol ? L"PaleMoonURL" : L"PaleMoonHTML"; - bool isDefault = !wcsicmp(registeredApp, progID); - CoTaskMemFree(registeredApp); - - return isDefault; -} - -static void -IsDefaultBrowserWin8(bool aCheckAllTypes, bool* aIsDefaultBrowser) -{ - RefPtr<IApplicationAssociationRegistration> pAAR; - HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration, - nullptr, - CLSCTX_INPROC, - IID_IApplicationAssociationRegistration, - getter_AddRefs(pAAR)); - if (FAILED(hr)) { - return; - } - - bool res = IsAARDefault(pAAR, L"http"); - if (*aIsDefaultBrowser) { - *aIsDefaultBrowser = res; - } - res = IsAARDefault(pAAR, L".html"); - if (*aIsDefaultBrowser && aCheckAllTypes) { - *aIsDefaultBrowser = res; - } -} - -/* - * Query's the AAR for the default status. - * This only checks for PaleMoonURL and if aCheckAllTypes is set, then - * it also checks for PaleMoonHTML. Note that those ProgIDs are shared - * by all PaleMoon browsers. -*/ -bool -nsWindowsShellService::IsDefaultBrowserVista(bool aCheckAllTypes, - bool* aIsDefaultBrowser) -{ - RefPtr<IApplicationAssociationRegistration> pAAR; - HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration, - nullptr, - CLSCTX_INPROC, - IID_IApplicationAssociationRegistration, - getter_AddRefs(pAAR)); - if (FAILED(hr)) { - return false; - } - - if (aCheckAllTypes) { - BOOL res; - hr = pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE, - APP_REG_NAME, - &res); - *aIsDefaultBrowser = res; - } else if (!IsWin8OrLater()) { - *aIsDefaultBrowser = IsAARDefault(pAAR, L"http"); - } - - return true; -} - -NS_IMETHODIMP -nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck, - bool aForAllTypes, - bool* aIsDefaultBrowser) -{ - // Assume we're the default unless one of the several checks below tell us - // otherwise. - *aIsDefaultBrowser = true; - - wchar_t exePath[MAX_BUF]; - if (!::GetModuleFileNameW(0, exePath, MAX_BUF)) - return NS_ERROR_FAILURE; - - // Convert the path to a long path since GetModuleFileNameW returns the path - // that was used to launch PaleMoon which is not necessarily a long path. - if (!::GetLongPathNameW(exePath, exePath, MAX_BUF)) - return NS_ERROR_FAILURE; - - nsAutoString appLongPath(exePath); - - HKEY theKey; - DWORD res; - nsresult rv; - wchar_t currValue[MAX_BUF]; - - SETTING* settings = gSettings; - if (!aForAllTypes && IsWin8OrLater()) { - // Skip over the file handler check - settings++; - } - - SETTING* end = gSettings + sizeof(gSettings) / sizeof(SETTING); - - for (; settings < end; ++settings) { - NS_ConvertUTF8toUTF16 keyName(settings->keyName); - NS_ConvertUTF8toUTF16 valueData(settings->valueData); - int32_t offset = valueData.Find("%APPPATH%"); - valueData.Replace(offset, 9, appLongPath); - - rv = OpenKeyForReading(HKEY_CLASSES_ROOT, keyName, &theKey); - if (NS_FAILED(rv)) { - *aIsDefaultBrowser = false; - return NS_OK; - } - - ::ZeroMemory(currValue, sizeof(currValue)); - DWORD len = sizeof currValue; - res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr, - (LPBYTE)currValue, &len); - // Close the key that was opened. - ::RegCloseKey(theKey); - if (REG_FAILED(res) || - _wcsicmp(valueData.get(), currValue)) { - // Key wasn't set or was set to something other than our registry entry. - NS_ConvertUTF8toUTF16 oldValueData(settings->oldValueData); - offset = oldValueData.Find("%APPPATH%"); - oldValueData.Replace(offset, 9, appLongPath); - // The current registry value doesn't match the current or the old format. - if (_wcsicmp(oldValueData.get(), currValue)) { - *aIsDefaultBrowser = false; - return NS_OK; - } - - res = ::RegOpenKeyExW(HKEY_CLASSES_ROOT, PromiseFlatString(keyName).get(), - 0, KEY_SET_VALUE, &theKey); - if (REG_FAILED(res)) { - // If updating the open command fails try to update it using the helper - // application when setting PaleMoon as the default browser. - *aIsDefaultBrowser = false; - return NS_OK; - } - - const nsString &flatValue = PromiseFlatString(valueData); - res = ::RegSetValueExW(theKey, L"", 0, REG_SZ, - (const BYTE *) flatValue.get(), - (flatValue.Length() + 1) * sizeof(char16_t)); - // Close the key that was created. - ::RegCloseKey(theKey); - if (REG_FAILED(res)) { - // If updating the open command fails try to update it using the helper - // application when setting PaleMoon as the default browser. - *aIsDefaultBrowser = false; - return NS_OK; - } - } - } - - // Only check if PaleMoon is the default browser on Vista and above if the - // previous checks show that PaleMoon is the default browser. - if (*aIsDefaultBrowser) { - IsDefaultBrowserVista(aForAllTypes, aIsDefaultBrowser); - if (IsWin8OrLater()) { - IsDefaultBrowserWin8(aForAllTypes, aIsDefaultBrowser); - } - } - - // To handle the case where DDE isn't disabled due for a user because there - // account didn't perform a PaleMoon update this will check if PaleMoon is the - // default browser and if dde is disabled for each handler - // and if it isn't disable it. When PaleMoon is not the default browser the - // helper application will disable dde for each handler. - if (*aIsDefaultBrowser && aForAllTypes) { - // Check ftp settings - - end = gDDESettings + sizeof(gDDESettings) / sizeof(SETTING); - - for (settings = gDDESettings; settings < end; ++settings) { - NS_ConvertUTF8toUTF16 keyName(settings->keyName); - - rv = OpenKeyForReading(HKEY_CURRENT_USER, keyName, &theKey); - if (NS_FAILED(rv)) { - ::RegCloseKey(theKey); - // If disabling DDE fails try to disable it using the helper - // application when setting PaleMoon as the default browser. - *aIsDefaultBrowser = false; - return NS_OK; - } - - ::ZeroMemory(currValue, sizeof(currValue)); - DWORD len = sizeof currValue; - res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr, - (LPBYTE)currValue, &len); - // Close the key that was opened. - ::RegCloseKey(theKey); - if (REG_FAILED(res) || char16_t('\0') != *currValue) { - // Key wasn't set or was set to something other than our registry entry. - // Delete the key along with all of its childrean and then recreate it. - const nsString &flatName = PromiseFlatString(keyName); - ::SHDeleteKeyW(HKEY_CURRENT_USER, flatName.get()); - res = ::RegCreateKeyExW(HKEY_CURRENT_USER, flatName.get(), 0, nullptr, - REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, - nullptr, &theKey, nullptr); - if (REG_FAILED(res)) { - // If disabling DDE fails try to disable it using the helper - // application when setting PaleMoon as the default browser. - *aIsDefaultBrowser = false; - return NS_OK; - } - - res = ::RegSetValueExW(theKey, L"", 0, REG_SZ, (const BYTE *) L"", - sizeof(char16_t)); - // Close the key that was created. - ::RegCloseKey(theKey); - if (REG_FAILED(res)) { - // If disabling DDE fails try to disable it using the helper - // application when setting PaleMoon as the default browser. - *aIsDefaultBrowser = false; - return NS_OK; - } - } - } - - // Update the FTP protocol handler's shell open command if it is the old - // format. - res = ::RegOpenKeyExW(HKEY_CURRENT_USER, FTP_SOC, 0, KEY_ALL_ACCESS, - &theKey); - // Don't update the FTP protocol handler's shell open command when opening - // its registry key fails under HKCU since it most likely doesn't exist. - if (NS_FAILED(rv)) { - return NS_OK; - } - - NS_ConvertUTF8toUTF16 oldValueOpen(OLD_VAL_OPEN); - int32_t offset = oldValueOpen.Find("%APPPATH%"); - oldValueOpen.Replace(offset, 9, appLongPath); - - ::ZeroMemory(currValue, sizeof(currValue)); - DWORD len = sizeof currValue; - res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr, (LPBYTE)currValue, - &len); - - // Don't update the FTP protocol handler's shell open command when the - // current registry value doesn't exist or matches the old format. - if (REG_FAILED(res) || - _wcsicmp(oldValueOpen.get(), currValue)) { - ::RegCloseKey(theKey); - return NS_OK; - } - - NS_ConvertUTF8toUTF16 valueData(VAL_OPEN); - valueData.Replace(offset, 9, appLongPath); - const nsString &flatValue = PromiseFlatString(valueData); - res = ::RegSetValueExW(theKey, L"", 0, REG_SZ, - (const BYTE *) flatValue.get(), - (flatValue.Length() + 1) * sizeof(char16_t)); - // Close the key that was created. - ::RegCloseKey(theKey); - // If updating the FTP protocol handlers shell open command fails try to - // update it using the helper application when setting PaleMoon as the - // default browser. - if (REG_FAILED(res)) { - *aIsDefaultBrowser = false; - } - } - - return NS_OK; -} - -static nsresult -DynSHOpenWithDialog(HWND hwndParent, const OPENASINFO *poainfo) -{ - // shell32.dll is in the knownDLLs list so will always be loaded from the - // system32 directory. - static const wchar_t kSehllLibraryName[] = L"shell32.dll"; - HMODULE shellDLL = ::LoadLibraryW(kSehllLibraryName); - if (!shellDLL) { - return NS_ERROR_FAILURE; - } - - decltype(SHOpenWithDialog)* SHOpenWithDialogFn = - (decltype(SHOpenWithDialog)*) GetProcAddress(shellDLL, "SHOpenWithDialog"); - - if (!SHOpenWithDialogFn) { - return NS_ERROR_FAILURE; - } - - nsresult rv; - HRESULT hr = SHOpenWithDialogFn(hwndParent, poainfo); - if (SUCCEEDED(hr) || (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED))) { - rv = NS_OK; - } else { - rv = NS_ERROR_FAILURE; - } - FreeLibrary(shellDLL); - return rv; -} - -nsresult -nsWindowsShellService::LaunchControlPanelDefaultsSelectionUI() -{ - IApplicationAssociationRegistrationUI* pAARUI; - HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistrationUI, - NULL, - CLSCTX_INPROC, - IID_IApplicationAssociationRegistrationUI, - (void**)&pAARUI); - if (SUCCEEDED(hr)) { - hr = pAARUI->LaunchAdvancedAssociationUI(APP_REG_NAME); - pAARUI->Release(); - } - return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE; -} - -nsresult -nsWindowsShellService::LaunchControlPanelDefaultPrograms() -{ - // Build the path control.exe path safely - WCHAR controlEXEPath[MAX_PATH + 1] = { '\0' }; - if (!GetSystemDirectoryW(controlEXEPath, MAX_PATH)) { - return NS_ERROR_FAILURE; - } - LPCWSTR controlEXE = L"control.exe"; - if (wcslen(controlEXEPath) + wcslen(controlEXE) >= MAX_PATH) { - return NS_ERROR_FAILURE; - } - if (!PathAppendW(controlEXEPath, controlEXE)) { - return NS_ERROR_FAILURE; - } - - WCHAR params[] = L"control.exe /name Microsoft.DefaultPrograms /page " - "pageDefaultProgram\\pageAdvancedSettings?pszAppName=" APP_REG_NAME; - STARTUPINFOW si = {sizeof(si), 0}; - si.dwFlags = STARTF_USESHOWWINDOW; - si.wShowWindow = SW_SHOWDEFAULT; - PROCESS_INFORMATION pi = {0}; - if (!CreateProcessW(controlEXEPath, params, nullptr, nullptr, FALSE, - 0, nullptr, nullptr, &si, &pi)) { - return NS_ERROR_FAILURE; - } - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - - return NS_OK; -} - -static bool -IsWindowsLogonConnected() -{ - WCHAR userName[UNLEN + 1]; - DWORD size = ArrayLength(userName); - if (!GetUserNameW(userName, &size)) { - return false; - } - - LPUSER_INFO_24 info; - if (NetUserGetInfo(nullptr, userName, 24, (LPBYTE *)&info) - != NERR_Success) { - return false; - } - bool connected = info->usri24_internet_identity; - NetApiBufferFree(info); - - return connected; -} - -static bool -SettingsAppBelievesConnected() -{ - nsresult rv; - nsCOMPtr<nsIWindowsRegKey> regKey = - do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv); - if (NS_FAILED(rv)) { - return false; - } - - rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, - NS_LITERAL_STRING("SOFTWARE\\Microsoft\\Windows\\Shell\\Associations"), - nsIWindowsRegKey::ACCESS_READ); - if (NS_FAILED(rv)) { - return false; - } - - uint32_t value; - rv = regKey->ReadIntValue(NS_LITERAL_STRING("IsConnectedAtLogon"), &value); - if (NS_FAILED(rv)) { - return false; - } - - return !!value; -} - -nsresult -nsWindowsShellService::LaunchModernSettingsDialogDefaultApps() -{ - if (!IsWindowsBuildOrLater(14965) && - !IsWindowsLogonConnected() && SettingsAppBelievesConnected()) { - // Use the classic Control Panel to work around a bug of older - // builds of Windows 10. - return LaunchControlPanelDefaultPrograms(); - } - - IApplicationActivationManager* pActivator; - HRESULT hr = CoCreateInstance(CLSID_ApplicationActivationManager, - nullptr, - CLSCTX_INPROC, - IID_IApplicationActivationManager, - (void**)&pActivator); - - if (SUCCEEDED(hr)) { - DWORD pid; - hr = pActivator->ActivateApplication( - L"windows.immersivecontrolpanel_cw5n1h2txyewy" - L"!microsoft.windows.immersivecontrolpanel", - L"page=SettingsPageAppsDefaults", AO_NONE, &pid); - if (SUCCEEDED(hr)) { - // Do not check error because we could at least open - // the "Default apps" setting. - pActivator->ActivateApplication( - L"windows.immersivecontrolpanel_cw5n1h2txyewy" - L"!microsoft.windows.immersivecontrolpanel", - L"page=SettingsPageAppsDefaults" - L"&target=SystemSettings_DefaultApps_Browser", AO_NONE, &pid); - } - pActivator->Release(); - return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE; - } - return NS_OK; -} - -nsresult -nsWindowsShellService::InvokeHTTPOpenAsVerb() -{ - nsCOMPtr<nsIURLFormatter> formatter( - do_GetService("@mozilla.org/toolkit/URLFormatterService;1")); - if (!formatter) { - return NS_ERROR_UNEXPECTED; - } - - nsString urlStr; - nsresult rv = formatter->FormatURLPref( - NS_LITERAL_STRING("app.support.baseURL"), urlStr); - if (NS_FAILED(rv)) { - return rv; - } - if (!StringBeginsWith(urlStr, NS_LITERAL_STRING("https://"))) { - return NS_ERROR_FAILURE; - } - urlStr.AppendLiteral("win10-default-browser"); - - SHELLEXECUTEINFOW seinfo = { sizeof(SHELLEXECUTEINFOW) }; - seinfo.lpVerb = L"openas"; - seinfo.lpFile = urlStr.get(); - seinfo.nShow = SW_SHOWNORMAL; - if (!ShellExecuteExW(&seinfo)) { - return NS_ERROR_FAILURE; - } - return NS_OK; -} - -nsresult -nsWindowsShellService::LaunchHTTPHandlerPane() -{ - OPENASINFO info; - info.pcszFile = L"http"; - info.pcszClass = nullptr; - info.oaifInFlags = OAIF_FORCE_REGISTRATION | - OAIF_URL_PROTOCOL | - OAIF_REGISTER_EXT; - return DynSHOpenWithDialog(nullptr, &info); -} - -NS_IMETHODIMP -nsWindowsShellService::SetDefaultBrowser(bool aClaimAllTypes, bool aForAllUsers) -{ - nsAutoString appHelperPath; - if (NS_FAILED(GetHelperPath(appHelperPath))) - return NS_ERROR_FAILURE; - - if (aForAllUsers) { - appHelperPath.AppendLiteral(" /SetAsDefaultAppGlobal"); - } else { - appHelperPath.AppendLiteral(" /SetAsDefaultAppUser"); - } - - nsresult rv = LaunchHelper(appHelperPath); - if (NS_SUCCEEDED(rv) && IsWin8OrLater()) { - if (aClaimAllTypes) { - if (IsWin10OrLater()) { - rv = LaunchModernSettingsDialogDefaultApps(); - } else { - rv = LaunchControlPanelDefaultsSelectionUI(); - } - // The above call should never really fail, but just in case - // fall back to showing the HTTP association screen only. - if (NS_FAILED(rv)) { - if (IsWin10OrLater()) { - rv = InvokeHTTPOpenAsVerb(); - } else { - rv = LaunchHTTPHandlerPane(); - } - } - } else { - // Windows 10 blocks attempts to load the - // HTTP Handler association dialog. - if (IsWin10OrLater()) { - rv = LaunchModernSettingsDialogDefaultApps(); - } else { - rv = LaunchHTTPHandlerPane(); - } - - // The above call should never really fail, but just in case - // fall back to showing control panel for all defaults - if (NS_FAILED(rv)) { - rv = LaunchControlPanelDefaultsSelectionUI(); - } - } - } - - nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID)); - if (prefs) { - (void) prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, true); - // Reset the number of times the dialog should be shown - // before it is silenced. - (void) prefs->SetIntPref(PREF_DEFAULTBROWSERCHECKCOUNT, 0); - } - - return rv; -} - -static nsresult -WriteBitmap(nsIFile* aFile, imgIContainer* aImage) -{ - nsresult rv; - - RefPtr<SourceSurface> surface = - aImage->GetFrame(imgIContainer::FRAME_FIRST, - imgIContainer::FLAG_SYNC_DECODE); - NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE); - - // For either of the following formats we want to set the biBitCount member - // of the BITMAPINFOHEADER struct to 32, below. For that value the bitmap - // format defines that the A8/X8 WORDs in the bitmap byte stream be ignored - // for the BI_RGB value we use for the biCompression member. - MOZ_ASSERT(surface->GetFormat() == SurfaceFormat::B8G8R8A8 || - surface->GetFormat() == SurfaceFormat::B8G8R8X8); - - RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface(); - NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE); - - int32_t width = dataSurface->GetSize().width; - int32_t height = dataSurface->GetSize().height; - int32_t bytesPerPixel = 4 * sizeof(uint8_t); - uint32_t bytesPerRow = bytesPerPixel * width; - - // initialize these bitmap structs which we will later - // serialize directly to the head of the bitmap file - BITMAPINFOHEADER bmi; - bmi.biSize = sizeof(BITMAPINFOHEADER); - bmi.biWidth = width; - bmi.biHeight = height; - bmi.biPlanes = 1; - bmi.biBitCount = (WORD)bytesPerPixel*8; - bmi.biCompression = BI_RGB; - bmi.biSizeImage = bytesPerRow * height; - bmi.biXPelsPerMeter = 0; - bmi.biYPelsPerMeter = 0; - bmi.biClrUsed = 0; - bmi.biClrImportant = 0; - - BITMAPFILEHEADER bf; - bf.bfType = 0x4D42; // 'BM' - bf.bfReserved1 = 0; - bf.bfReserved2 = 0; - bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); - bf.bfSize = bf.bfOffBits + bmi.biSizeImage; - - // get a file output stream - nsCOMPtr<nsIOutputStream> stream; - rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), aFile); - NS_ENSURE_SUCCESS(rv, rv); - - DataSourceSurface::MappedSurface map; - if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) { - return NS_ERROR_FAILURE; - } - - // write the bitmap headers and rgb pixel data to the file - rv = NS_ERROR_FAILURE; - if (stream) { - uint32_t written; - stream->Write((const char*)&bf, sizeof(BITMAPFILEHEADER), &written); - if (written == sizeof(BITMAPFILEHEADER)) { - stream->Write((const char*)&bmi, sizeof(BITMAPINFOHEADER), &written); - if (written == sizeof(BITMAPINFOHEADER)) { - // write out the image data backwards because the desktop won't - // show bitmaps with negative heights for top-to-bottom - uint32_t i = map.mStride * height; - do { - i -= map.mStride; - stream->Write(((const char*)map.mData) + i, bytesPerRow, &written); - if (written == bytesPerRow) { - rv = NS_OK; - } else { - rv = NS_ERROR_FAILURE; - break; - } - } while (i != 0); - } - } - - stream->Close(); - } - - dataSurface->Unmap(); - - return rv; -} - -NS_IMETHODIMP -nsWindowsShellService::SetDesktopBackground(nsIDOMElement* aElement, - int32_t aPosition) -{ - nsresult rv; - - nsCOMPtr<imgIContainer> container; - nsCOMPtr<nsIDOMHTMLImageElement> imgElement(do_QueryInterface(aElement)); - if (!imgElement) { - // XXX write background loading stuff! - return NS_ERROR_NOT_AVAILABLE; - } - else { - nsCOMPtr<nsIImageLoadingContent> imageContent = - do_QueryInterface(aElement, &rv); - if (!imageContent) - return rv; - - // get the image container - nsCOMPtr<imgIRequest> request; - rv = imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, - getter_AddRefs(request)); - if (!request) - return rv; - rv = request->GetImage(getter_AddRefs(container)); - if (!container) - return NS_ERROR_FAILURE; - } - - // get the file name from localized strings - nsCOMPtr<nsIStringBundleService> - bundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr<nsIStringBundle> shellBundle; - rv = bundleService->CreateBundle(SHELLSERVICE_PROPERTIES, - getter_AddRefs(shellBundle)); - NS_ENSURE_SUCCESS(rv, rv); - - // e.g. "Desktop Background.bmp" - nsString fileLeafName; - rv = shellBundle->GetStringFromName - (u"desktopBackgroundLeafNameWin", - getter_Copies(fileLeafName)); - NS_ENSURE_SUCCESS(rv, rv); - - // get the profile root directory - nsCOMPtr<nsIFile> file; - rv = NS_GetSpecialDirectory(NS_APP_APPLICATION_REGISTRY_DIR, - getter_AddRefs(file)); - NS_ENSURE_SUCCESS(rv, rv); - - // eventually, the path is "%APPDATA%\Mozilla\PaleMoon\Desktop Background.bmp" - rv = file->Append(fileLeafName); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoString path; - rv = file->GetPath(path); - NS_ENSURE_SUCCESS(rv, rv); - - // write the bitmap to a file in the profile directory - rv = WriteBitmap(file, container); - - // if the file was written successfully, set it as the system wallpaper - if (NS_SUCCEEDED(rv)) { - nsCOMPtr<nsIWindowsRegKey> regKey = - do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - - rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, - NS_LITERAL_STRING("Control Panel\\Desktop"), - nsIWindowsRegKey::ACCESS_SET_VALUE); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoString tile; - nsAutoString style; - switch (aPosition) { - case BACKGROUND_TILE: - style.Assign('0'); - tile.Assign('1'); - break; - case BACKGROUND_CENTER: - style.Assign('0'); - tile.Assign('0'); - break; - case BACKGROUND_STRETCH: - style.Assign('2'); - tile.Assign('0'); - break; - case BACKGROUND_FILL: - style.AssignLiteral("10"); - tile.Assign('0'); - break; - case BACKGROUND_FIT: - style.Assign('6'); - tile.Assign('0'); - break; - } - - rv = regKey->WriteStringValue(NS_LITERAL_STRING("TileWallpaper"), tile); - NS_ENSURE_SUCCESS(rv, rv); - rv = regKey->WriteStringValue(NS_LITERAL_STRING("WallpaperStyle"), style); - NS_ENSURE_SUCCESS(rv, rv); - rv = regKey->Close(); - NS_ENSURE_SUCCESS(rv, rv); - - ::SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, (PVOID)path.get(), - SPIF_UPDATEINIFILE | SPIF_SENDCHANGE); - } - return rv; -} - -NS_IMETHODIMP -nsWindowsShellService::OpenApplication(int32_t aApplication) -{ - nsAutoString application; - switch (aApplication) { - case nsIShellService::APPLICATION_MAIL: - application.AssignLiteral("Mail"); - break; - case nsIShellService::APPLICATION_NEWS: - application.AssignLiteral("News"); - break; - } - - // The Default Client section of the Windows Registry looks like this: - // - // Clients\aClient\ - // e.g. aClient = "Mail"... - // \Mail\(default) = Client Subkey Name - // \Client Subkey Name - // \Client Subkey Name\shell\open\command\ - // \Client Subkey Name\shell\open\command\(default) = path to exe - // - - // Find the default application for this class. - HKEY theKey; - nsresult rv = OpenKeyForReading(HKEY_CLASSES_ROOT, application, &theKey); - if (NS_FAILED(rv)) - return rv; - - wchar_t buf[MAX_BUF]; - DWORD type, len = sizeof buf; - DWORD res = ::RegQueryValueExW(theKey, EmptyString().get(), 0, - &type, (LPBYTE)&buf, &len); - - if (REG_FAILED(res) || !*buf) - return NS_OK; - - // Close the key we opened. - ::RegCloseKey(theKey); - - // Find the "open" command - application.Append('\\'); - application.Append(buf); - application.AppendLiteral("\\shell\\open\\command"); - - rv = OpenKeyForReading(HKEY_CLASSES_ROOT, application, &theKey); - if (NS_FAILED(rv)) - return rv; - - ::ZeroMemory(buf, sizeof(buf)); - len = sizeof buf; - res = ::RegQueryValueExW(theKey, EmptyString().get(), 0, - &type, (LPBYTE)&buf, &len); - if (REG_FAILED(res) || !*buf) - return NS_ERROR_FAILURE; - - // Close the key we opened. - ::RegCloseKey(theKey); - - // Look for any embedded environment variables and substitute their - // values, as |::CreateProcessW| is unable to do this. - nsAutoString path(buf); - int32_t end = path.Length(); - int32_t cursor = 0, temp = 0; - ::ZeroMemory(buf, sizeof(buf)); - do { - cursor = path.FindChar('%', cursor); - if (cursor < 0) - break; - - temp = path.FindChar('%', cursor + 1); - ++cursor; - - ::ZeroMemory(&buf, sizeof(buf)); - - ::GetEnvironmentVariableW(nsAutoString(Substring(path, cursor, temp - cursor)).get(), - buf, sizeof(buf)); - - // "+ 2" is to subtract the extra characters used to delimit the environment - // variable ('%'). - path.Replace((cursor - 1), temp - cursor + 2, nsDependentString(buf)); - - ++cursor; - } - while (cursor < end); - - STARTUPINFOW si; - PROCESS_INFORMATION pi; - - ::ZeroMemory(&si, sizeof(STARTUPINFOW)); - ::ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); - - BOOL success = ::CreateProcessW(nullptr, (LPWSTR)path.get(), nullptr, - nullptr, FALSE, 0, nullptr, nullptr, - &si, &pi); - if (!success) - return NS_ERROR_FAILURE; - - return NS_OK; -} - -NS_IMETHODIMP -nsWindowsShellService::GetDesktopBackgroundColor(uint32_t* aColor) -{ - uint32_t color = ::GetSysColor(COLOR_DESKTOP); - *aColor = (GetRValue(color) << 16) | (GetGValue(color) << 8) | GetBValue(color); - return NS_OK; -} - -NS_IMETHODIMP -nsWindowsShellService::SetDesktopBackgroundColor(uint32_t aColor) -{ - int aParameters[2] = { COLOR_BACKGROUND, COLOR_DESKTOP }; - BYTE r = (aColor >> 16); - BYTE g = (aColor << 16) >> 24; - BYTE b = (aColor << 24) >> 24; - COLORREF colors[2] = { RGB(r,g,b), RGB(r,g,b) }; - - ::SetSysColors(sizeof(aParameters) / sizeof(int), aParameters, colors); - - nsresult rv; - nsCOMPtr<nsIWindowsRegKey> regKey = - do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - - rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, - NS_LITERAL_STRING("Control Panel\\Colors"), - nsIWindowsRegKey::ACCESS_SET_VALUE); - NS_ENSURE_SUCCESS(rv, rv); - - wchar_t rgb[12]; - _snwprintf(rgb, 12, L"%u %u %u", r, g, b); - - rv = regKey->WriteStringValue(NS_LITERAL_STRING("Background"), - nsDependentString(rgb)); - NS_ENSURE_SUCCESS(rv, rv); - - return regKey->Close(); -} - -nsWindowsShellService::nsWindowsShellService() -{ -} - -nsWindowsShellService::~nsWindowsShellService() -{ -} - -NS_IMETHODIMP -nsWindowsShellService::OpenApplicationWithURI(nsIFile* aApplication, - const nsACString& aURI) -{ - nsresult rv; - nsCOMPtr<nsIProcess> process = - do_CreateInstance("@mozilla.org/process/util;1", &rv); - if (NS_FAILED(rv)) - return rv; - - rv = process->Init(aApplication); - if (NS_FAILED(rv)) - return rv; - - const nsCString spec(aURI); - const char* specStr = spec.get(); - return process->Run(false, &specStr, 1); -} - -NS_IMETHODIMP -nsWindowsShellService::GetDefaultFeedReader(nsIFile** _retval) -{ - *_retval = nullptr; - - nsresult rv; - nsCOMPtr<nsIWindowsRegKey> regKey = - do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - - rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, - NS_LITERAL_STRING("feed\\shell\\open\\command"), - nsIWindowsRegKey::ACCESS_READ); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoString path; - rv = regKey->ReadStringValue(EmptyString(), path); - NS_ENSURE_SUCCESS(rv, rv); - if (path.IsEmpty()) - return NS_ERROR_FAILURE; - - if (path.First() == '"') { - // Everything inside the quotes - path = Substring(path, 1, path.FindChar('"', 1) - 1); - } - else { - // Everything up to the first space - path = Substring(path, 0, path.FindChar(' ')); - } - - nsCOMPtr<nsIFile> defaultReader = - do_CreateInstance("@mozilla.org/file/local;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - - rv = defaultReader->InitWithPath(path); - NS_ENSURE_SUCCESS(rv, rv); - - bool exists; - rv = defaultReader->Exists(&exists); - NS_ENSURE_SUCCESS(rv, rv); - if (!exists) - return NS_ERROR_FAILURE; - - NS_ADDREF(*_retval = defaultReader); - return NS_OK; -} diff --git a/components/shell/nsWindowsShellService.h b/components/shell/nsWindowsShellService.h deleted file mode 100644 index 06c6c3c..0000000 --- a/components/shell/nsWindowsShellService.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef nswindowsshellservice_h____ -#define nswindowsshellservice_h____ - -#include "nscore.h" -#include "nsStringAPI.h" -#include "nsIWindowsShellService.h" -#include "nsITimer.h" - -#include <windows.h> -#include <ole2.h> - -class nsWindowsShellService : public nsIWindowsShellService -{ - virtual ~nsWindowsShellService(); - -public: - nsWindowsShellService(); - - NS_DECL_ISUPPORTS - NS_DECL_NSISHELLSERVICE - NS_DECL_NSIWINDOWSSHELLSERVICE - -protected: - bool IsDefaultBrowserVista(bool aCheckAllTypes, bool* aIsDefaultBrowser); - nsresult LaunchControlPanelDefaultsSelectionUI(); - nsresult LaunchControlPanelDefaultPrograms(); - nsresult LaunchModernSettingsDialogDefaultApps(); - nsresult InvokeHTTPOpenAsVerb(); - nsresult LaunchHTTPHandlerPane(); -}; - -#endif // nswindowsshellservice_h____ diff --git a/components/statusbar/Downloads.jsm b/components/statusbar/Downloads.jsm deleted file mode 100644 index 091fdad..0000000 --- a/components/statusbar/Downloads.jsm +++ /dev/null @@ -1,674 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const EXPORTED_SYMBOLS = ["S4EDownloadService"]; - -const CC = Components.classes; -const CI = Components.interfaces; -const CU = Components.utils; - -CU.import("resource://gre/modules/Services.jsm"); -CU.import("resource://gre/modules/PluralForm.jsm"); -CU.import("resource://gre/modules/DownloadUtils.jsm"); -CU.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); -CU.import("resource://gre/modules/XPCOMUtils.jsm"); - -function S4EDownloadService(window, gBrowser, service, getters) -{ - this._window = window; - this._gBrowser = gBrowser; - this._service = service; - this._getters = getters; - - this._handler = new JSTransferHandler(this._window, this); -} - -S4EDownloadService.prototype = -{ - _window: null, - _gBrowser: null, - _service: null, - _getters: null, - - _handler: null, - _listening: false, - - _binding: false, - _customizing: false, - - _lastTime: Infinity, - - _dlActive: false, - _dlPaused: false, - _dlFinished: false, - - _dlCountStr: null, - _dlTimeStr: null, - - _dlProgressAvg: 0, - _dlProgressMax: 0, - _dlProgressMin: 0, - _dlProgressType: "active", - - _dlNotifyTimer: 0, - _dlNotifyGlowTimer: 0, - - init: function() - { - if(!this._getters.downloadButton) - { - this.uninit(); - return; - } - - if(this._listening) - { - return; - } - - this._handler.start(); - this._listening = true; - - this._lastTime = Infinity; - - this.updateBinding(); - this.updateStatus(); - }, - - uninit: function() - { - if(!this._listening) - { - return; - } - - this._listening = false; - this._handler.stop(); - - this.releaseBinding(); - }, - - destroy: function() - { - this.uninit(); - this._handler.destroy(); - - ["_window", "_gBrowser", "_service", "_getters", "_handler"].forEach(function(prop) - { - delete this[prop]; - }, this); - }, - - updateBinding: function() - { - if(!this._listening) - { - this.releaseBinding(); - return; - } - - switch(this._service.downloadButtonAction) - { - case 1: // Default - this.attachBinding(); - break; - default: - this.releaseBinding(); - break; - } - }, - - attachBinding: function() - { - if(this._binding) - { - return; - } - - let db = this._window.DownloadsButton; - - db._getAnchorS4EBackup = db.getAnchor; - db.getAnchor = this.getAnchor.bind(this); - - db._releaseAnchorS4EBackup = db.releaseAnchor; - db.releaseAnchor = function() {}; - - this._binding = true; - }, - - releaseBinding: function() - { - if(!this._binding) - { - return; - } - - let db = this._window.DownloadsButton; - - db.getAnchor = db._getAnchorS4EBackup; - db.releaseAnchor = db._releaseAnchorS4EBackup; - - this._binding = false; - }, - - customizing: function(val) - { - this._customizing = val; - }, - - updateStatus: function(lastFinished) - { - if(!this._getters.downloadButton) - { - this.uninit(); - return; - } - - let numActive = 0; - let numPaused = 0; - let activeTotalSize = 0; - let activeTransferred = 0; - let activeMaxProgress = -Infinity; - let activeMinProgress = Infinity; - let pausedTotalSize = 0; - let pausedTransferred = 0; - let pausedMaxProgress = -Infinity; - let pausedMinProgress = Infinity; - let maxTime = -Infinity; - - let dls = ((this.isPrivateWindow) ? this._handler.activePrivateEntries() : this._handler.activeEntries()); - for(let dl of dls) - { - if(dl.state == CI.nsIDownloadManager.DOWNLOAD_DOWNLOADING) - { - numActive++; - if(dl.size > 0) - { - if(dl.speed > 0) - { - maxTime = Math.max(maxTime, (dl.size - dl.transferred) / dl.speed); - } - - activeTotalSize += dl.size; - activeTransferred += dl.transferred; - - let currentProgress = ((dl.transferred * 100) / dl.size); - activeMaxProgress = Math.max(activeMaxProgress, currentProgress); - activeMinProgress = Math.min(activeMinProgress, currentProgress); - } - } - else if(dl.state == CI.nsIDownloadManager.DOWNLOAD_PAUSED) - { - numPaused++; - if(dl.size > 0) - { - pausedTotalSize += dl.size; - pausedTransferred += dl.transferred; - - let currentProgress = ((dl.transferred * 100) / dl.size); - pausedMaxProgress = Math.max(pausedMaxProgress, currentProgress); - pausedMinProgress = Math.min(pausedMinProgress, currentProgress); - } - } - } - - if((numActive + numPaused) == 0) - { - this._dlActive = false; - this._dlFinished = lastFinished; - this.updateButton(); - this._lastTime = Infinity; - return; - } - - let dlPaused = (numActive == 0); - let dlStatus = ((dlPaused) ? this._getters.strings.getString("pausedDownloads") - : this._getters.strings.getString("activeDownloads")); - let dlCount = ((dlPaused) ? numPaused : numActive); - let dlTotalSize = ((dlPaused) ? pausedTotalSize : activeTotalSize); - let dlTransferred = ((dlPaused) ? pausedTransferred : activeTransferred); - let dlMaxProgress = ((dlPaused) ? pausedMaxProgress : activeMaxProgress); - let dlMinProgress = ((dlPaused) ? pausedMinProgress : activeMinProgress); - let dlProgressType = ((dlPaused) ? "paused" : "active"); - - [this._dlTimeStr, this._lastTime] = DownloadUtils.getTimeLeft(maxTime, this._lastTime); - this._dlCountStr = PluralForm.get(dlCount, dlStatus).replace("#1", dlCount); - this._dlProgressAvg = ((dlTotalSize == 0) ? 100 : ((dlTransferred * 100) / dlTotalSize)); - this._dlProgressMax = ((dlTotalSize == 0) ? 100 : dlMaxProgress); - this._dlProgressMin = ((dlTotalSize == 0) ? 100 : dlMinProgress); - this._dlProgressType = dlProgressType + ((dlTotalSize == 0) ? "-unknown" : ""); - this._dlPaused = dlPaused; - this._dlActive = true; - this._dlFinished = false; - - this.updateButton(); - }, - - updateButton: function() - { - let download_button = this._getters.downloadButton; - let download_tooltip = this._getters.downloadButtonTooltip; - let download_progress = this._getters.downloadButtonProgress; - let download_label = this._getters.downloadButtonLabel; - if(!download_button) - { - return; - } - - if(!this._dlActive) - { - download_button.collapsed = true; - download_label.value = download_tooltip.label = this._getters.strings.getString("noDownloads"); - - download_progress.collapsed = true; - download_progress.value = 0; - - if(this._dlFinished && this._handler.hasPBAPI && !this.isUIShowing) - { - this.callAttention(download_button); - } - return; - } - - switch(this._service.downloadProgress) - { - case 2: - download_progress.value = this._dlProgressMax; - break; - case 3: - download_progress.value = this._dlProgressMin; - break; - default: - download_progress.value = this._dlProgressAvg; - break; - } - download_progress.setAttribute("pmType", this._dlProgressType); - download_progress.collapsed = (this._service.downloadProgress == 0); - - download_label.value = this.buildString(this._service.downloadLabel); - download_tooltip.label = this.buildString(this._service.downloadTooltip); - - this.clearAttention(download_button); - download_button.collapsed = false; - }, - - callAttention: function(download_button) - { - if(this._dlNotifyGlowTimer != 0) - { - this._window.clearTimeout(this._dlNotifyGlowTimer); - this._dlNotifyGlowTimer = 0; - } - - download_button.setAttribute("attention", "true"); - - if(this._service.downloadNotifyTimeout) - { - this._dlNotifyGlowTimer = this._window.setTimeout(function(self, button) - { - self._dlNotifyGlowTimer = 0; - button.removeAttribute("attention"); - }, this._service.downloadNotifyTimeout, this, download_button); - } - }, - - clearAttention: function(download_button) - { - if(this._dlNotifyGlowTimer != 0) - { - this._window.clearTimeout(this._dlNotifyGlowTimer); - this._dlNotifyGlowTimer = 0; - } - - download_button.removeAttribute("attention"); - }, - - notify: function() - { - if(this._dlNotifyTimer == 0 && this._service.downloadNotifyAnimate) - { - let download_button_anchor = this._getters.downloadButtonAnchor; - let download_notify_anchor = this._getters.downloadNotifyAnchor; - if(download_button_anchor) - { - if(!download_notify_anchor.style.transform) - { - let bAnchorRect = download_button_anchor.getBoundingClientRect(); - let nAnchorRect = download_notify_anchor.getBoundingClientRect(); - - let translateX = bAnchorRect.left - nAnchorRect.left; - translateX += .5 * (bAnchorRect.width - nAnchorRect.width); - - let translateY = bAnchorRect.top - nAnchorRect.top; - translateY += .5 * (bAnchorRect.height - nAnchorRect.height); - - download_notify_anchor.style.transform = "translate(" + translateX + "px, " + translateY + "px)"; - } - - download_notify_anchor.setAttribute("notification", "finish"); - this._dlNotifyTimer = this._window.setTimeout(function(self, anchor) - { - self._dlNotifyTimer = 0; - anchor.removeAttribute("notification"); - anchor.style.transform = ""; - }, 1000, this, download_notify_anchor); - } - } - }, - - clearFinished: function() - { - this._dlFinished = false; - let download_button = this._getters.downloadButton; - if(download_button) - { - this.clearAttention(download_button); - } - }, - - getAnchor: function(aCallback) - { - if(this._customizing) - { - aCallback(null); - return; - } - - aCallback(this._getters.downloadButtonAnchor); - }, - - openUI: function(aEvent) - { - this.clearFinished(); - - switch(this._service.downloadButtonAction) - { - case 1: // Firefox Default - this._handler.openUINative(); - break; - case 2: // Show Library - this._window.PlacesCommandHook.showPlacesOrganizer("Downloads"); - break; - case 3: // Show Tab - let found = this._gBrowser.browsers.some(function(browser, index) - { - if("about:downloads" == browser.currentURI.spec) - { - this._gBrowser.selectedTab = this._gBrowser.tabContainer.childNodes[index]; - return true; - } - }, this); - - if(!found) - { - this._window.openUILinkIn("about:downloads", "tab"); - } - break; - case 4: // External Command - let command = this._service.downloadButtonActionCommand; - if(commend) - { - this._window.goDoCommand(command); - } - break; - default: // Nothing - break; - } - - aEvent.stopPropagation(); - }, - - get isPrivateWindow() - { - return this._handler.hasPBAPI && PrivateBrowsingUtils.isWindowPrivate(this._window); - }, - - get isUIShowing() - { - switch(this._service.downloadButtonAction) - { - case 1: // Firefox Default - return this._handler.isUIShowingNative; - case 2: // Show Library - var organizer = Services.wm.getMostRecentWindow("Places:Organizer"); - if(organizer) - { - let selectedNode = organizer.PlacesOrganizer._places.selectedNode; - let downloadsItemId = organizer.PlacesUIUtils.leftPaneQueries["Downloads"]; - return selectedNode && selectedNode.itemId === downloadsItemId; - } - return false; - case 3: // Show tab - let currentURI = this._gBrowser.currentURI; - return currentURI && currentURI.spec == "about:downloads"; - default: // Nothing - return false; - } - }, - - buildString: function(mode) - { - switch(mode) - { - case 0: - return this._dlCountStr; - case 1: - return ((this._dlPaused) ? this._dlCountStr : this._dlTimeStr); - default: - let compStr = this._dlCountStr; - if(!this._dlPaused) - { - compStr += " (" + this._dlTimeStr + ")"; - } - return compStr; - } - } -}; - -function JSTransferHandler(window, downloadService) -{ - this._window = window; - - let api = CU.import("resource://gre/modules/Downloads.jsm", {}).Downloads; - - this._activePublic = new JSTransferListener(downloadService, api.getList(api.PUBLIC), false); - this._activePrivate = new JSTransferListener(downloadService, api.getList(api.PRIVATE), true); -} - -JSTransferHandler.prototype = -{ - _window: null, - _activePublic: null, - _activePrivate: null, - - destroy: function() - { - this._activePublic.destroy(); - this._activePrivate.destroy(); - - ["_window", "_activePublic", "_activePrivate"].forEach(function(prop) - { - delete this[prop]; - }, this); - }, - - start: function() - { - this._activePublic.start(); - this._activePrivate.start(); - }, - - stop: function() - { - this._activePublic.stop(); - this._activePrivate.stop(); - }, - - get hasPBAPI() - { - return true; - }, - - openUINative: function() - { - this._window.DownloadsPanel.showPanel(); - }, - - get isUIShowingNative() - { - return this._window.DownloadsPanel.isPanelShowing; - }, - - activeEntries: function() - { - return this._activePublic.downloads(); - }, - - activePrivateEntries: function() - { - return this._activePrivate.downloads(); - } -}; - -function JSTransferListener(downloadService, listPromise, isPrivate) -{ - this._downloadService = downloadService; - this._isPrivate = isPrivate; - this._downloads = new Map(); - - listPromise.then(this.initList.bind(this)).then(null, CU.reportError); -} - -JSTransferListener.prototype = -{ - _downloadService: null, - _list: null, - _downloads: null, - _isPrivate: false, - _wantsStart: false, - - initList: function(list) - { - this._list = list; - if(this._wantsStart) { - this.start(); - } - - this._list.getAll().then(this.initDownloads.bind(this)).then(null, CU.reportError); - }, - - initDownloads: function(downloads) - { - downloads.forEach(function(download) - { - this.onDownloadAdded(download); - }, this); - }, - - destroy: function() - { - this._downloads.clear(); - - ["_downloadService", "_list", "_downloads"].forEach(function(prop) - { - delete this[prop]; - }, this); - }, - - start: function() - { - if(!this._list) - { - this._wantsStart = true; - return; - } - - this._list.addView(this); - }, - - stop: function() - { - if(!this._list) - { - this._wantsStart = false; - return; - } - - this._list.removeView(this); - }, - - downloads: function() - { - return this._downloads.values(); - }, - - convertToState: function(dl) - { - if(dl.succeeded) - { - return CI.nsIDownloadManager.DOWNLOAD_FINISHED; - } - if(dl.error && dl.error.becauseBlockedByParentalControls) - { - return CI.nsIDownloadManager.DOWNLOAD_BLOCKED_PARENTAL; - } - if(dl.error) - { - return CI.nsIDownloadManager.DOWNLOAD_FAILED; - } - if(dl.canceled && dl.hasPartialData) - { - return CI.nsIDownloadManager.DOWNLOAD_PAUSED; - } - if(dl.canceled) - { - return CI.nsIDownloadManager.DOWNLOAD_CANCELED; - } - if(dl.stopped) - { - return CI.nsIDownloadManager.DOWNLOAD_NOTSTARTED; - } - return CI.nsIDownloadManager.DOWNLOAD_DOWNLOADING; - }, - - onDownloadAdded: function(aDownload) - { - let dl = this._downloads.get(aDownload); - if(!dl) - { - dl = {}; - this._downloads.set(aDownload, dl); - } - - dl.state = this.convertToState(aDownload); - dl.size = aDownload.totalBytes; - dl.speed = aDownload.speed; - dl.transferred = aDownload.currentBytes; - }, - - onDownloadChanged: function(aDownload) - { - this.onDownloadAdded(aDownload); - - if(this._isPrivate != this._downloadService.isPrivateWindow) - { - return; - } - - this._downloadService.updateStatus(aDownload.succeeded); - - if(aDownload.succeeded) - { - this._downloadService.notify() - } - }, - - onDownloadRemoved: function(aDownload) - { - this._downloads.delete(aDownload); - } -}; - diff --git a/components/statusbar/Progress.jsm b/components/statusbar/Progress.jsm deleted file mode 100644 index 69d55db..0000000 --- a/components/statusbar/Progress.jsm +++ /dev/null @@ -1,183 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const EXPORTED_SYMBOLS = ["S4EProgressService"]; - -const CI = Components.interfaces; -const CU = Components.utils; - -CU.import("resource://gre/modules/XPCOMUtils.jsm"); - -function S4EProgressService(gBrowser, service, getters, statusService) { - this._gBrowser = gBrowser; - this._service = service; - this._getters = getters; - this._statusService = statusService; - - this._gBrowser.addProgressListener(this); -} - -S4EProgressService.prototype = -{ - _gBrowser: null, - _service: null, - _getters: null, - _statusService: null, - - _busyUI: false, - - set value(val) - { - let toolbar_progress = this._getters.toolbarProgress; - if(toolbar_progress) - { - toolbar_progress.value = val; - } - - let throbber_progress = this._getters.throbberProgress; - if(throbber_progress) - { - if(val) - { - throbber_progress.setAttribute("progress", val); - } - else - { - throbber_progress.removeAttribute("progress"); - } - } - }, - - set collapsed(val) - { - let toolbar_progress = this._getters.toolbarProgress; - if(toolbar_progress) - { - toolbar_progress.collapsed = val; - } - - let throbber_progress = this._getters.throbberProgress; - if(throbber_progress) - { - if(val) - { - throbber_progress.removeAttribute("busy"); - } - else - { - throbber_progress.setAttribute("busy", true); - } - } - }, - - destroy: function() - { - this._gBrowser.removeProgressListener(this); - - ["_gBrowser", "_service", "_getters", "_statusService"].forEach(function(prop) - { - delete this[prop]; - }, this); - }, - - onStatusChange: function(aWebProgress, aRequest, aStatus, aMessage) - { - this._statusService.setNetworkStatus(aMessage, this._busyUI); - }, - - onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) - { - let nsIWPL = CI.nsIWebProgressListener; - - if(!this._busyUI - && aStateFlags & nsIWPL.STATE_START - && aStateFlags & nsIWPL.STATE_IS_NETWORK - && !(aStateFlags & nsIWPL.STATE_RESTORING)) - { - this._busyUI = true; - this.value = 0; - this.collapsed = false; - } - else if(aStateFlags & nsIWPL.STATE_STOP) - { - if(aRequest) - { - let msg = ""; - let location; - if(aRequest instanceof CI.nsIChannel || "URI" in aRequest) - { - location = aRequest.URI; - if(location.spec != "about:blank") - { - switch (aStatus) - { - case Components.results.NS_BINDING_ABORTED: - msg = this._getters.strings.getString("nv_stopped"); - break; - case Components.results.NS_ERROR_NET_TIMEOUT: - msg = this._getters.strings.getString("nv_timeout"); - break; - } - } - } - - if(!msg && (!location || location.spec != "about:blank")) - { - msg = this._getters.strings.getString("nv_done"); - } - - this._statusService.setDefaultStatus(msg); - this._statusService.setNetworkStatus("", this._busyUI); - } - - if(this._busyUI) - { - this._busyUI = false; - this.collapsed = true; - this.value = 0; - } - } - }, - - onUpdateCurrentBrowser: function(aStateFlags, aStatus, aMessage, aTotalProgress) - { - let nsIWPL = CI.nsIWebProgressListener; - let loadingDone = aStateFlags & nsIWPL.STATE_STOP; - - this.onStateChange( - this._gBrowser.webProgress, - { URI: this._gBrowser.currentURI }, - ((loadingDone ? nsIWPL.STATE_STOP : nsIWPL.STATE_START) | (aStateFlags & nsIWPL.STATE_IS_NETWORK)), - aStatus - ); - - if(!loadingDone) - { - this.onProgressChange(this._gBrowser.webProgress, null, 0, 0, aTotalProgress, 1); - this.onStatusChange(this._gBrowser.webProgress, null, 0, aMessage); - } - }, - - onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) - { - if (aMaxTotalProgress > 0 && this._busyUI) - { - // This is highly optimized. Don't touch this code unless - // you are intimately familiar with the cost of setting - // attrs on XUL elements. -- hyatt - let percentage = (aCurTotalProgress * 100) / aMaxTotalProgress; - this.value = percentage; - } - }, - - onProgressChange64: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) - { - return this.onProgressChange(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress); - }, - - QueryInterface: XPCOMUtils.generateQI([ CI.nsIWebProgressListener, CI.nsIWebProgressListener2 ]) -}; - diff --git a/components/statusbar/Status.jsm b/components/statusbar/Status.jsm deleted file mode 100644 index dbdd1fc..0000000 --- a/components/statusbar/Status.jsm +++ /dev/null @@ -1,492 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const EXPORTED_SYMBOLS = ["S4EStatusService"]; - -const CU = Components.utils; - -CU.import("resource://gre/modules/Services.jsm"); -CU.import("resource://gre/modules/XPCOMUtils.jsm"); - -function S4EStatusService(window, service, getters) -{ - this._window = window; - this._service = service; - this._getters = getters; - - this._overLinkService = new S4EOverlinkService(this._window, this._service, this); -} - -S4EStatusService.prototype = -{ - _window: null, - _service: null, - _getters: null, - _overLinkService: null, - - _overLink: { val: "", type: "" }, - _network: { val: "", type: "" }, - _networkXHR: { val: "", type: "" }, - _status: { val: "", type: "" }, - _jsStatus: { val: "", type: "" }, - _defaultStatus: { val: "", type: "" }, - - _isFullScreen: false, - _isVideo: false, - - _statusText: { val: "", type: "" }, - _noUpdate: false, - _statusChromeTimeoutID: 0, - _statusContentTimeoutID: 0, - - getCompositeStatusText: function() - { - return this._statusText.val; - }, - - getStatusText: function() - { - return this._status.val; - }, - - setNetworkStatus: function(status, busy) - { - if(busy) - { - this._network = { val: status, type: "network" }; - this._networkXHR = { val: "", type: "network xhr" }; - } - else - { - this._networkXHR = { val: status, type: "network xhr" }; - } - this.updateStatusField(); - }, - - setStatusText: function(status) - { - this._status = { val: status, type: "status chrome" }; - this.updateStatusField(); - }, - - setJSStatus: function(status) - { - this._jsStatus = { val: status, type: "status content" }; - this.updateStatusField(); - }, - - setJSDefaultStatus: function(status) - { - // This was removed from Firefox in Bug 862917 - }, - - setDefaultStatus: function(status) - { - this._defaultStatus = { val: status, type: "status chrome default" }; - this.updateStatusField(); - }, - - setOverLink: function(link, aAnchor) - { - this._overLinkService.update(link, aAnchor); - }, - - setOverLinkInternal: function(link, aAnchor) - { - let status = this._service.status; - let statusLinkOver = this._service.statusLinkOver; - - if(statusLinkOver) - { - link = link.replace(/[\u200e\u200f\u202a\u202b\u202c\u202d\u202e]/g, encodeURIComponent); - if(status == statusLinkOver) - { - this._overLink = { val: link, type: "overLink", anchor: aAnchor }; - this.updateStatusField(); - } - else - { - this.setStatusField(statusLinkOver, { val: link, type: "overLink", anchor: aAnchor }, true); - } - } - }, - - setNoUpdate: function(nu) - { - this._noUpdate = nu; - }, - - buildBinding: function() { - - // Object.prototype.watch() shim, based on Eli Grey's polyfill - // object.watch - if (!this._window.XULBrowserWindow.watch) { - Object.defineProperty(this._window.XULBrowserWindow, "watch", { - enumerable: false, - configurable: true, - writable: false, - value: function (prop, handler) { - var oldval = this[prop], - newval = oldval, - getter = function () { - return newval; - }, - setter = function (val) { - oldval = newval; - return newval = handler.call(this, prop, oldval, val); - } - ; - - try { - if (delete this[prop]) { // can't watch constants - Object.defineProperty(this, prop, { - get: getter, - set: setter, - enumerable: true, - configurable: true - }); - } - } catch(e) { - // This fails fatally on non-configurable props, so just - // ignore errors if it does. - } - } - }); - } - - // object.unwatch - if (!this._window.XULBrowserWindow.unwatch) { - Object.defineProperty(this._window.XULBrowserWindow, "unwatch", { - enumerable: false, - configurable: true, - writable: false, - value: function (prop) { - var val = this[prop]; - delete this[prop]; // remove accessors - this[prop] = val; - } - }); - } - - let XULBWPropHandler = function(prop, oldval, newval) { - CU.reportError("Attempt to modify XULBrowserWindow." + prop); - return oldval; - }; - - ["updateStatusField", "onStatusChange"].forEach(function(prop) - { - this._window.XULBrowserWindow.unwatch(prop); - this._window.XULBrowserWindow[prop] = function() {}; - this._window.XULBrowserWindow.watch(prop, XULBWPropHandler); - }, this); - - ["getCompositeStatusText", "getStatusText", "setStatusText", "setJSStatus", - "setJSDefaultStatus", "setDefaultStatus", "setOverLink"].forEach(function(prop) - { - this._window.XULBrowserWindow.unwatch(prop); - this._window.XULBrowserWindow[prop] = this[prop].bind(this); - this._window.XULBrowserWindow.watch(prop, XULBWPropHandler); - }, this); - }, - - destroy: function() - { - // No need to unbind from the XULBrowserWindow, it's already null at this point - - this.clearTimer("_statusChromeTimeoutID"); - this.clearTimer("_statusContentTimeoutID"); - - this._overLinkService.destroy(); - - ["_overLink", "_network", "_networkXHR", "_status", "_jsStatus", "_defaultStatus", - "_statusText", "_window", "_service", "_getters", "_overLinkService"].forEach(function(prop) - { - delete this[prop]; - }, this); - }, - - buildTextOrder: function() - { - this.__defineGetter__("_textOrder", function() - { - let textOrder = ["_overLink"]; - if(this._service.statusNetwork) - { - textOrder.push("_network"); - if(this._service.statusNetworkXHR) - { - textOrder.push("_networkXHR"); - } - } - textOrder.push("_status", "_jsStatus"); - if(this._service.statusDefault) - { - textOrder.push("_defaultStatus"); - } - - delete this._textOrder; - return this._textOrder = textOrder; - }); - }, - - updateStatusField: function(force) - { - let text = { val: "", type: "" }; - for(let i = 0; !text.val && i < this._textOrder.length; i++) - { - text = this[this._textOrder[i]]; - } - - if(this._statusText.val != text.val || force) - { - if(this._noUpdate) - { - return; - } - - this._statusText = text; - - this.setStatusField(this._service.status, text, false); - - if(text.val && this._service.statusTimeout) - { - this.setTimer(text.type); - } - } - }, - - setFullScreenState: function(isFullScreen, isVideo) - { - this._isFullScreen = isFullScreen; - this._isVideo = isFullScreen && isVideo; - - this.clearStatusField(); - this.updateStatusField(true); - }, - - setTimer: function(type) - { - let typeArgs = type.split(" ", 3); - - if(typeArgs.length < 2 || typeArgs[0] != "status") - { - return; - } - - if(typeArgs[1] == "chrome") - { - this.clearTimer("_statusChromeTimeoutID"); - this._statusChromeTimeoutID = this._window.setTimeout(function(self, isDefault) - { - self._statusChromeTimeoutID = 0; - if(isDefault) - { - self.setDefaultStatus(""); - } - else - { - self.setStatusText(""); - } - }, this._service.statusTimeout, this, (typeArgs.length == 3 && typeArgs[2] == "default")); - } - else - { - this.clearTimer("_statusContentTimeoutID"); - this._statusContentTimeoutID = this._window.setTimeout(function(self) - { - self._statusContentTimeoutID = 0; - self.setJSStatus(""); - }, this._service.statusTimeout, this); - } - }, - - clearTimer: function(timerName) - { - if(this[timerName] != 0) - { - this._window.clearTimeout(this[timerName]); - this[timerName] = 0; - } - }, - - clearStatusField: function() - { - this._getters.statusOverlay.value = ""; - - let status_label = this._getters.statusWidgetLabel; - if(status_label) - { - status_label.value = ""; - } - - }, - - setStatusField: function(location, text, allowTooltip) - { - if(!location) - { - return; - } - - let label = null; - - if(this._isFullScreen) - { - switch(location) - { - case 1: // Toolbar - location = 3 - break; - case 2: // URL bar - if(Services.prefs.getBoolPref("browser.fullscreen.autohide")) - { - location = 3 - } - break; - } - } - - switch(location) - { - case 1: // Toolbar - label = this._getters.statusWidgetLabel; - break; - case 2: // URL Bar - break; - case 3: // Popup - default: - if(this._isVideo) - { - return; - } - label = this._getters.statusOverlay; - break; - } - - if(label) - { - label.setAttribute("previoustype", label.getAttribute("type")); - label.setAttribute("type", text.type); - label.value = text.val; - label.setAttribute("crop", text.type == "overLink" ? "center" : "end"); - } - } -}; - -function S4EOverlinkService(window, service, statusService) { - this._window = window; - this._service = service; - this._statusService = statusService; -} - -S4EOverlinkService.prototype = -{ - _window: null, - _service: null, - _statusService: null, - - _timer: 0, - _currentLink: { link: "", anchor: null }, - _pendingLink: { link: "", anchor: null }, - _listening: false, - - update: function(aLink, aAnchor) - { - this.clearTimer(); - this.stopListen(); - this._pendingLink = { link: aLink, anchor: aAnchor }; - - if(!aLink) - { - if(this._window.XULBrowserWindow.hideOverLinkImmediately || !this._service.statusLinkOverDelayHide) - { - this._show(); - } - else - { - this._showDelayed(); - } - } - else if(this._currentLink.link || !this._service.statusLinkOverDelayShow) - { - this._show(); - } - else - { - this._showDelayed(); - this.startListen(); - } - }, - - destroy: function() - { - this.clearTimer(); - this.stopListen(); - - ["_currentLink", "_pendingLink", "_statusService", "_window"].forEach(function(prop) - { - delete this[prop]; - }, this); - }, - - startListen: function() - { - if(!this._listening) - { - this._window.addEventListener("mousemove", this, true); - this._listening = true; - } - }, - - stopListen: function() - { - if(this._listening) - { - this._window.removeEventListener("mousemove", this, true); - this._listening = false; - } - }, - - clearTimer: function() - { - if(this._timer != 0) - { - this._window.clearTimeout(this._timer); - this._timer = 0; - } - }, - - handleEvent: function(event) - { - switch(event.type) - { - case "mousemove": - this.clearTimer(); - this._showDelayed(); - } - }, - - _showDelayed: function() - { - let delay = ((this._pendingLink.link) - ? this._service.statusLinkOverDelayShow - : this._service.statusLinkOverDelayHide); - - this._timer = this._window.setTimeout(function(self) - { - self._timer = 0; - self._show(); - self.stopListen(); - }, delay, this); - }, - - _show: function() - { - this._currentLink = this._pendingLink; - this._statusService.setOverLinkInternal(this._currentLink.link, this._currentLink.anchor); - } -}; - diff --git a/components/statusbar/Status4Evar.jsm b/components/statusbar/Status4Evar.jsm deleted file mode 100644 index 6400f2e..0000000 --- a/components/statusbar/Status4Evar.jsm +++ /dev/null @@ -1,312 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const EXPORTED_SYMBOLS = ["Status4Evar"]; - -const CC = Components.classes; -const CI = Components.interfaces; -const CU = Components.utils; - -const s4e_service = CC["@caligonstudios.com/status4evar;1"].getService(CI.nsIStatus4Evar); - -CU.import("resource://gre/modules/Services.jsm"); -CU.import("resource://gre/modules/XPCOMUtils.jsm"); -CU.import("resource://gre/modules/AddonManager.jsm"); - -CU.import("resource:///modules/statusbar/Status.jsm"); -CU.import("resource:///modules/statusbar/Progress.jsm"); -CU.import("resource:///modules/statusbar/Downloads.jsm"); -CU.import("resource:///modules/statusbar/Toolbars.jsm"); - -function Status4Evar(window, gBrowser, toolbox) -{ - this._window = window; - this._toolbox = toolbox; - - this.getters = new S4EWindowGetters(this._window); - this.toolbars = new S4EToolbars(this._window, gBrowser, this._toolbox, s4e_service, this.getters); - this.statusService = new S4EStatusService(this._window, s4e_service, this.getters); - this.progressMeter = new S4EProgressService(gBrowser, s4e_service, this.getters, this.statusService); - this.downloadStatus = new S4EDownloadService(this._window, gBrowser, s4e_service, this.getters); - this.sizeModeService = new SizeModeService(this._window, gBrowser, this); - - this._window.addEventListener("unload", this, false); -} - -Status4Evar.prototype = -{ - _window: null, - _toolbox: null, - - getters: null, - toolbars: null, - statusService: null, - progressMeter: null, - downloadStatus: null, - sizeModeService: null, - - setup: function() - { - this._toolbox.addEventListener("beforecustomization", this, false); - this._toolbox.addEventListener("aftercustomization", this, false); - - this.toolbars.setup(); - this.updateWindow(); - - // OMFG HAX! If a page is already loading, fake a network start event - if(this._window.XULBrowserWindow._busyUI) - { - let nsIWPL = CI.nsIWebProgressListener; - this.progressMeter.onStateChange(0, null, nsIWPL.STATE_START | nsIWPL.STATE_IS_NETWORK, 0); - } - }, - - destroy: function() - { - this._window.removeEventListener("unload", this, false); - this._toolbox.removeEventListener("aftercustomization", this, false); - this._toolbox.removeEventListener("beforecustomization", this, false); - - this.getters.destroy(); - this.statusService.destroy(); - this.downloadStatus.destroy(); - this.progressMeter.destroy(); - this.toolbars.destroy(); - this.sizeModeService.destroy(); - - ["_window", "_toolbox", "getters", "statusService", "downloadStatus", - "progressMeter", "toolbars", "sizeModeService"].forEach(function(prop) - { - delete this[prop]; - }, this); - }, - - handleEvent: function(aEvent) - { - switch(aEvent.type) - { - case "unload": - this.destroy(); - break; - case "beforecustomization": - this.beforeCustomization(); - break; - case "aftercustomization": - this.updateWindow(); - break; - } - }, - - beforeCustomization: function() - { - this.toolbars.updateSplitters(false); - this.toolbars.updateWindowGripper(false); - - this.statusService.setNoUpdate(true); - let status_label = this.getters.statusWidgetLabel; - if(status_label) - { - status_label.value = this.getters.strings.getString("statusText"); - } - - this.downloadStatus.customizing(true); - }, - - updateWindow: function() - { - this.statusService.setNoUpdate(false); - this.getters.resetGetters(); - this.statusService.buildTextOrder(); - this.statusService.buildBinding(); - this.downloadStatus.init(); - this.downloadStatus.customizing(false); - this.toolbars.updateSplitters(true); - - s4e_service.updateWindow(this._window); - // This also handles the following: - // * buildTextOrder() - // * updateStatusField(true) - // * updateWindowGripper(true) - }, - - launchOptions: function(currentWindow) - { - let optionsURL = "chrome://browser/content/statusbar/prefs.xul"; - let windows = Services.wm.getEnumerator(null); - while (windows.hasMoreElements()) - { - let win = windows.getNext(); - if (win.document.documentURI == optionsURL) - { - win.focus(); - return; - } - } - - let features = "chrome,titlebar,toolbar,centerscreen"; - try - { - let instantApply = Services.prefs.getBoolPref("browser.preferences.instantApply"); - features += instantApply ? ",dialog=no" : ",modal"; - } - catch(e) - { - features += ",modal"; - } - currentWindow.openDialog(optionsURL, "", features); - } - -}; - -function S4EWindowGetters(window) -{ - this._window = window; -} - -S4EWindowGetters.prototype = -{ - _window: null, - _getterMap: - [ - ["addonbar", "addon-bar"], - ["addonbarCloseButton", "addonbar-closebutton"], - ["browserBottomBox", "browser-bottombox"], - ["downloadButton", "status4evar-download-button"], - ["downloadButtonTooltip", "status4evar-download-tooltip"], - ["downloadButtonProgress", "status4evar-download-progress-bar"], - ["downloadButtonLabel", "status4evar-download-label"], - ["downloadButtonAnchor", "status4evar-download-anchor"], - ["downloadNotifyAnchor", "status4evar-download-notification-anchor"], - ["statusBar", "status4evar-status-bar"], - ["statusWidget", "status4evar-status-widget"], - ["statusWidgetLabel", "status4evar-status-text"], - ["strings", "bundle_status4evar"], - ["throbberProgress", "status4evar-throbber-widget"], - ["toolbarProgress", "status4evar-progress-bar"] - ], - - resetGetters: function() - { - let document = this._window.document; - - this._getterMap.forEach(function(getter) - { - let [prop, id] = getter; - delete this[prop]; - this.__defineGetter__(prop, function() - { - delete this[prop]; - return this[prop] = document.getElementById(id); - }); - }, this); - - delete this.statusOverlay; - this.__defineGetter__("statusOverlay", function() - { - let so = this._window.XULBrowserWindow.statusTextField; - if(!so) - { - return null; - } - - delete this.statusOverlay; - return this.statusOverlay = so; - }); - }, - - destroy: function() - { - this._getterMap.forEach(function(getter) - { - let [prop, id] = getter; - delete this[prop]; - }, this); - - ["statusOverlay", "statusOverlay", "_window"].forEach(function(prop) - { - delete this[prop]; - }, this); - } -}; - -function SizeModeService(window, gBrowser, s4e) -{ - this._window = window; - this._gBrowser = gBrowser; - this._s4e = s4e; - this._mm = this._window.messageManager; - - this.lastFullScreen = this._window.fullScreen; - this.lastwindowState = this._window.windowState; - - if(s4e_service.advancedStatusDetectFullScreen) - { - this._mm.addMessageListener("status4evar@caligonstudios.com:video-detect-answer", this) - this._mm.loadFrameScript("resource:///modules/statusbar/content-thunk.js", true); - } - - this._window.addEventListener("sizemodechange", this, false); -} - -SizeModeService.prototype = -{ - _window: null, - _gBrowser: null, - _s4e: null, - _mm: null, - - lastFullScreen: null, - lastwindowState: null, - - destroy: function() - { - this._window.removeEventListener("sizemodechange", this, false); - - if(s4e_service.advancedStatusDetectFullScreen) - { - this._mm.removeDelayedFrameScript("resource:///modules/statusbar/content-thunk.js"); - this._mm.removeMessageListener("status4evar@caligonstudios.com:video-detect-answer", this); - } - - ["_window", "_gBrowser", "_s4e", "_mm"].forEach(function(prop) - { - delete this[prop]; - }, this); - }, - - handleEvent: function(e) - { - if(this._window.fullScreen != this.lastFullScreen && s4e_service.advancedStatusDetectFullScreen) - { - this.lastFullScreen = this._window.fullScreen; - - if(this.lastFullScreen && s4e_service.advancedStatusDetectVideo) - { - this._gBrowser.selectedBrowser.messageManager.sendAsyncMessage("status4evar@caligonstudios.com:video-detect"); - } - else - { - this._s4e.statusService.setFullScreenState(this.lastFullScreen, false); - } - } - - if(this._window.windowState != this.lastwindowState) - { - this.lastwindowState = this._window.windowState; - this._s4e.toolbars.updateWindowGripper(true); - } - }, - - receiveMessage: function(message) - { - if(message.name == "status4evar@caligonstudios.com:video-detect-answer") - { - this._s4e.statusService.setFullScreenState(this.lastFullScreen, message.data.isVideo); - } - }, - - QueryInterface: XPCOMUtils.generateQI([ CI.nsIDOMEventListener, CI.nsIMessageListener ]) -}; diff --git a/components/statusbar/Toolbars.jsm b/components/statusbar/Toolbars.jsm deleted file mode 100644 index 321efd0..0000000 --- a/components/statusbar/Toolbars.jsm +++ /dev/null @@ -1,221 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const EXPORTED_SYMBOLS = ["S4EToolbars"]; - -const CI = Components.interfaces; -const CU = Components.utils; - -CU.import("resource://gre/modules/Services.jsm"); - -function S4EToolbars(window, gBrowser, toolbox, service, getters) -{ - this._window = window; - this._toolbox = toolbox; - this._service = service; - this._getters = getters; - this._handler = new ClassicS4EToolbars(this._window, this._toolbox); -} - -S4EToolbars.prototype = -{ - _window: null, - _toolbox: null, - _service: null, - _getters: null, - - _handler: null, - - setup: function() - { - this.updateSplitters(false); - this.updateWindowGripper(false); - this._handler.setup(this._service.firstRun); - }, - - destroy: function() - { - this._handler.destroy(); - - ["_window", "_toolbox", "_service", "_getters", "_handler"].forEach(function(prop) - { - delete this[prop]; - }, this); - }, - - updateSplitters: function(action) - { - let document = this._window.document; - - let splitter_before = document.getElementById("status4evar-status-splitter-before"); - if(splitter_before) - { - splitter_before.parentNode.removeChild(splitter_before); - } - - let splitter_after = document.getElementById("status4evar-status-splitter-after"); - if(splitter_after) - { - splitter_after.parentNode.removeChild(splitter_after); - } - - let status = this._getters.statusWidget; - if(!action || !status) - { - return; - } - - let urlbar = document.getElementById("urlbar-container"); - let stop = document.getElementById("stop-button"); - let fullscreenflex = document.getElementById("fullscreenflex"); - - let nextSibling = status.nextSibling; - let previousSibling = status.previousSibling; - - function getSplitter(splitter, suffix) - { - if(!splitter) - { - splitter = document.createElement("splitter"); - splitter.id = "status4evar-status-splitter-" + suffix; - splitter.setAttribute("resizebefore", "flex"); - splitter.setAttribute("resizeafter", "flex"); - splitter.className = "chromeclass-toolbar-additional status4evar-status-splitter"; - } - return splitter; - } - - if((previousSibling && previousSibling.flex > 0) - || (urlbar && stop && urlbar.getAttribute("combined") && stop == previousSibling)) - { - status.parentNode.insertBefore(getSplitter(splitter_before, "before"), status); - } - - if(nextSibling && nextSibling.flex > 0 && nextSibling != fullscreenflex) - { - status.parentNode.insertBefore(getSplitter(splitter_after, "after"), nextSibling); - } - }, - - updateWindowGripper: function(action) - { - let document = this._window.document; - - let gripper = document.getElementById("status4evar-window-gripper"); - let toolbar = this._getters.statusBar || this._getters.addonbar; - - if(!action || !toolbar || !this._service.addonbarWindowGripper - || this._window.windowState != CI.nsIDOMChromeWindow.STATE_NORMAL || toolbar.toolbox.customizing) - { - if(gripper) - { - gripper.parentNode.removeChild(gripper); - } - return; - } - - gripper = this._handler.buildGripper(toolbar, gripper, "status4evar-window-gripper"); - - toolbar.appendChild(gripper); - } -}; - -function ClassicS4EToolbars(window, toolbox) -{ - this._window = window; - this._toolbox = toolbox; -} - -ClassicS4EToolbars.prototype = -{ - _window: null, - _toolbox: null, - - setup: function(firstRun) - { - let document = this._window.document; - - let addon_bar = document.getElementById("addon-bar"); - if(addon_bar) - { - let baseSet = "addonbar-closebutton" - + ",status4evar-status-widget" - + ",status4evar-progress-widget"; - - // Update the defaultSet - let defaultSet = baseSet; - let defaultSetIgnore = ["addonbar-closebutton", "spring", "status-bar"]; - addon_bar.getAttribute("defaultset").split(",").forEach(function(item) - { - if(defaultSetIgnore.indexOf(item) == -1) - { - defaultSet += "," + item; - } - }); - defaultSet += ",status-bar" - addon_bar.setAttribute("defaultset", defaultSet); - - // Update the currentSet - if(firstRun) - { - let isCustomizableToolbar = function(aElt) - { - return aElt.localName == "toolbar" && aElt.getAttribute("customizable") == "true"; - } - - let isCustomizedAlready = false; - let toolbars = Array.filter(this._toolbox.childNodes, isCustomizableToolbar).concat( - Array.filter(this._toolbox.externalToolbars, isCustomizableToolbar)); - toolbars.forEach(function(toolbar) - { - if(toolbar.currentSet.indexOf("status4evar") > -1) - { - isCustomizedAlready = true; - } - }); - - if(!isCustomizedAlready) - { - let currentSet = baseSet; - let currentSetIgnore = ["addonbar-closebutton", "spring"]; - addon_bar.currentSet.split(",").forEach(function(item) - { - if(currentSetIgnore.indexOf(item) == -1) - { - currentSet += "," + item; - } - }); - addon_bar.currentSet = currentSet; - addon_bar.setAttribute("currentset", currentSet); - document.persist(addon_bar.id, "currentset"); - this._window.setToolbarVisibility(addon_bar, true); - } - } - } - }, - - destroy: function() - { - ["_window", "_toolbox"].forEach(function(prop) - { - delete this[prop]; - }, this); - }, - - buildGripper: function(toolbar, gripper, id) - { - if(!gripper) - { - let document = this._window.document; - - gripper = document.createElement("resizer"); - gripper.id = id; - gripper.dir = "bottomend"; - } - - return gripper; - } -}; diff --git a/components/statusbar/content-thunk.js b/components/statusbar/content-thunk.js deleted file mode 100644 index fe1fbab..0000000 --- a/components/statusbar/content-thunk.js +++ /dev/null @@ -1,23 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -function handleVideoDetect(message) -{ - let isVideo = false; - - let fsEl = content.document.mozFullScreenElement; - if(fsEl) - { - isVideo = ( - fsEl.nodeName == "VIDEO" - || (fsEl.nodeName == "IFRAME" && fsEl.contentDocument && fsEl.contentDocument.getElementsByTagName("VIDEO").length > 0) - || fsEl.getElementsByTagName("VIDEO").length > 0 - ); - } - - sendAsyncMessage("status4evar@caligonstudios.com:video-detect-answer", {isVideo: isVideo}); -} - -addMessageListener("status4evar@caligonstudios.com:video-detect", handleVideoDetect); - diff --git a/components/statusbar/content/overlay.css b/components/statusbar/content/overlay.css deleted file mode 100644 index fd34521..0000000 --- a/components/statusbar/content/overlay.css +++ /dev/null @@ -1,14 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); - -/* - * Status Popup - */ - -statuspanel { - -moz-binding: url("chrome://browser/content/statusbar/tabbrowser.xml#statuspanel"); -} - diff --git a/components/statusbar/content/overlay.js b/components/statusbar/content/overlay.js deleted file mode 100644 index b868aaf..0000000 --- a/components/statusbar/content/overlay.js +++ /dev/null @@ -1,16 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -if(!caligon) var caligon = {}; - -window.addEventListener("load", function buildS4E() -{ - window.removeEventListener("load", buildS4E, false); - - Components.utils.import("resource:///modules/statusbar/Status4Evar.jsm"); - - caligon.status4evar = new Status4Evar(window, gBrowser, gNavToolbox); - caligon.status4evar.setup(); -}, false); - diff --git a/components/statusbar/content/overlay.xul b/components/statusbar/content/overlay.xul deleted file mode 100644 index b9934ee..0000000 --- a/components/statusbar/content/overlay.xul +++ /dev/null @@ -1,82 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<!-- This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<!DOCTYPE overlay SYSTEM "chrome://browser/locale/statusbar/statusbar-overlay.dtd"> - -<?xml-stylesheet href="chrome://browser/content/statusbar/overlay.css" type="text/css" ?> -<?xml-stylesheet href="chrome://browser/skin/statusbar/overlay.css" type="text/css" ?> -<?xml-stylesheet href="chrome://browser/skin/statusbar/dynamic.css" type="text/css" ?> - -<overlay id="status4evar-overlay" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - - <stringbundleset id="stringbundleset"> - <stringbundle id="bundle_status4evar" src="chrome://browser/locale/statusbar/overlay.properties" /> - </stringbundleset> - - <script type="application/javascript" src="chrome://browser/content/statusbar/overlay.js" /> - - <commandset> - <command id="S4E:Options" oncommand="caligon.status4evar.launchOptions(window);"/> - </commandset> - - <popupset id="mainPopupSet"> - <hbox id="status4evar-download-notification-container" mousethrough="always"> - <vbox id="status4evar-download-notification-anchor"> - <vbox id="status4evar-download-notification-icon" /> - </vbox> - </hbox> - </popupset> - - <menupopup id="menu_ToolsPopup"> - <menuitem id="statusbar-options-fx" command="S4E:Options" - label="&status4evar.menu.options.label;"/> - </menupopup> - - <menupopup id="appmenu_customizeMenu"> - <menuitem id="statusbar-options-app" command="S4E:Options" - label="&status4evar.menu.options.label;"/> - </menupopup> - - <toolbarpalette id="BrowserToolbarPalette"> - <toolbaritem id="status4evar-status-widget" - title="&status4evar.status.widget.title;" - removable="true" flex="1" persist="width" width="100"> - <label id="status4evar-status-text" flex="1" crop="end" value="&status4evar.status.widget.title;" /> - </toolbaritem> - - <toolbarbutton id="status4evar-download-button" - title="&status4evar.download.widget.title;" - class="toolbarbutton-1 chromeclass-toolbar-additional" - removable="true" collapsed="true" tooltip="_child" - oncommand="caligon.status4evar.downloadStatus.openUI(event)"> - <stack id="status4evar-download-anchor" class="toolbarbutton-icon"> - <vbox id="status4evar-download-icon" /> - <vbox pack="end"> - <progressmeter id="status4evar-download-progress-bar" mode="normal" value="0" collapsed="true" min="0" max="100" /> - </vbox> - </stack> - <tooltip id="status4evar-download-tooltip" /> - <label id="status4evar-download-label" value="&status4evar.download.widget.title;" class="toolbarbutton-text" crop="right" flex="1" /> - </toolbarbutton> - - <toolbaritem id="status4evar-progress-widget" - title="&status4evar.progress.widget.title;" - removable="true"> - <progressmeter id="status4evar-progress-bar" class="progressmeter-statusbar" - mode="normal" value="0" collapsed="true" min="0" max="100" /> - </toolbaritem> - - <toolbarbutton id="status4evar-options-button" - title="&status4evar.options.widget.title;" - class="toolbarbutton-1 chromeclass-toolbar-additional" - label="&status4evar.options.widget.label;" - removable="true" command="S4E:Options" tooltiptext="&status4evar.options.widget.title;" /> - </toolbarpalette> - - <statusbar id="status-bar" ordinal="1" /> -</overlay> - diff --git a/components/statusbar/content/prefs.css b/components/statusbar/content/prefs.css deleted file mode 100644 index bafaa61..0000000 --- a/components/statusbar/content/prefs.css +++ /dev/null @@ -1,10 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); - -.css-bg-editor { - -moz-binding: url("chrome://browser/content/statusbar/prefs.xml#css-bg-editor"); -} - diff --git a/components/statusbar/content/prefs.js b/components/statusbar/content/prefs.js deleted file mode 100644 index 47fd4b6..0000000 --- a/components/statusbar/content/prefs.js +++ /dev/null @@ -1,274 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -Components.utils.import("resource://gre/modules/Services.jsm"); - -var status4evarPrefs = -{ - get dynamicProgressStyle() - { - let styleSheets = window.document.styleSheets; - for(let i = 0; i < styleSheets.length; i++) - { - let styleSheet = styleSheets[i]; - if(styleSheet.href == "chrome://browser/skin/statusbar/dynamic.css") - { - delete this.dynamicProgressStyle; - return this.dynamicProgressStyle = styleSheet; - } - } - - return null; - }, - -// -// Status timeout management -// - get statusTimeoutPref() - { - delete this.statusTimeoutPref; - return this.statusTimeoutPref = document.getElementById("status4evar-pref-status-timeout"); - }, - - get statusTimeoutCheckbox() - { - delete this.statusTimeoutCheckbox; - return this.statusTimeoutCheckbox = document.getElementById("status4evar-status-timeout-check"); - }, - - statusTimeoutChanged: function() - { - if(this.statusTimeoutPref.value > 0) - { - this.statusTimeoutPref.disabled = false; - this.statusTimeoutCheckbox.checked = true; - } - else - { - this.statusTimeoutPref.disabled = true; - this.statusTimeoutCheckbox.checked = false; - } - }, - - statusTimeoutSync: function() - { - this.statusTimeoutChanged(); - return undefined; - }, - - statusTimeoutToggle: function() - { - if(this.statusTimeoutPref.disabled == this.statusTimeoutCheckbox.checked) - { - if(this.statusTimeoutCheckbox.checked) - { - this.statusTimeoutPref.value = 10; - } - else - { - this.statusTimeoutPref.value = 0; - } - } - }, - -// -// Status network management -// - get statusNetworkPref() - { - delete this.statusNetworkPref; - return this.statusNetworkPref = document.getElementById("status4evar-pref-status-network"); - }, - - get statusNetworkXHRPref() - { - delete this.statusNetworkXHRPref; - return this.statusNetworkXHRPref = document.getElementById("status4evar-pref-status-network-xhr"); - }, - - statusNetworkChanged: function() - { - this.statusNetworkXHRPref.disabled = ! this.statusNetworkPref.value; - }, - - statusNetworkSync: function() - { - this.statusNetworkChanged(); - return undefined; - }, - -// -// Status Text langth managment -// - get textMaxLengthPref() - { - delete this.textMaxLengthPref; - return this.textMaxLengthPref = document.getElementById("status4evar-pref-status-toolbar-maxLength"); - }, - - get textMaxLengthCheckbox() - { - delete this.textMaxLengthCheckbox; - return this.textMaxLengthCheckbox = document.getElementById("status4evar-status-toolbar-maxLength-check"); - }, - - textLengthChanged: function() - { - if(this.textMaxLengthPref.value > 0) - { - this.textMaxLengthPref.disabled = false; - this.textMaxLengthCheckbox.checked = true; - } - else - { - this.textMaxLengthPref.disabled = true; - this.textMaxLengthCheckbox.checked = false; - } - }, - - textLengthSync: function() - { - this.textLengthChanged(); - return undefined; - }, - - textLengthToggle: function() - { - if(this.textMaxLengthPref.disabled == this.textMaxLengthCheckbox.checked) - { - if(this.textMaxLengthCheckbox.checked) - { - this.textMaxLengthPref.value = 800; - } - else - { - this.textMaxLengthPref.value = 0; - } - } - }, - -// -// Toolbar progress style management -// - get progressToolbarStylePref() - { - delete this.progressToolbarStylePref; - return this.progressToolbarStylePref = document.getElementById("status4evar-pref-progress-toolbar-style"); - }, - - get progressToolbarCSSPref() - { - delete this.progressToolbarCSSPref; - return this.progressToolbarCSSPref = document.getElementById("status4evar-pref-progress-toolbar-css"); - }, - - get progressToolbarProgress() - { - delete this.progressToolbarProgress; - return this.progressToolbarProgress = document.getElementById("status4evar-progress-bar"); - }, - - progressToolbarCSSChanged: function() - { - if(!this.progressToolbarCSSPref.value) - { - this.progressToolbarCSSPref.value = "#33FF33"; - } - this.dynamicProgressStyle.cssRules[1].style.background = this.progressToolbarCSSPref.value; - }, - - progressToolbarStyleChanged: function() - { - this.progressToolbarCSSChanged(); - this.progressToolbarCSSPref.disabled = !this.progressToolbarStylePref.value; - if(this.progressToolbarStylePref.value) - { - this.progressToolbarProgress.setAttribute("s4estyle", true); - } - else - { - this.progressToolbarProgress.removeAttribute("s4estyle"); - } - }, - - progressToolbarStyleSync: function() - { - this.progressToolbarStyleChanged(); - return undefined; - }, - -// -// Download progress management -// - get downloadProgressCheck() - { - delete this.downloadProgressCheck; - return this.downloadProgressCheck = document.getElementById("status4evar-download-progress-check"); - }, - - get downloadProgressPref() - { - delete this.downloadProgressPref; - return this.downloadProgressPref = document.getElementById("status4evar-pref-download-progress"); - }, - - get downloadProgressColorActivePref() - { - delete this.downloadProgressActiveColorPref; - return this.downloadProgressActiveColorPref = document.getElementById("status4evar-pref-download-color-active"); - }, - - get downloadProgressColorPausedPref() - { - delete this.downloadProgressPausedColorPref; - return this.downloadProgressPausedColorPref = document.getElementById("status4evar-pref-download-color-paused"); - }, - - downloadProgressSync: function() - { - let val = this.downloadProgressPref.value; - this.downloadProgressColorActivePref.disabled = (val == 0); - this.downloadProgressColorPausedPref.disabled = (val == 0); - this.downloadProgressPref.disabled = (val == 0); - this.downloadProgressCheck.checked = (val != 0); - return ((val == 0) ? 1 : val); - }, - - downloadProgressToggle: function() - { - let enabled = this.downloadProgressCheck.checked; - this.downloadProgressPref.value = ((enabled) ? 1 : 0); - }, - -// -// Pref Window load -// - get downloadButtonActionCommandPref() - { - delete this.downloadButtonActionCommandPref; - return this.downloadButtonActionCommandPref = document.getElementById("status4evar-pref-download-button-action-command"); - }, - - get downloadButtonActionThirdPartyItem() - { - delete this.downloadButtonActionThirdPartyItem; - return this.downloadButtonActionThirdPartyItem = document.getElementById("status4evar-download-button-action-menu-thirdparty"); - }, - - onPrefWindowLoad: function() - { - if(!this.downloadButtonActionCommandPref.value) - { - this.downloadButtonActionThirdPartyItem.disabled = true; - } - }, - - onPrefWindowUnLoad: function() - { - } -} - -var XULBrowserWindow = { -} - diff --git a/components/statusbar/content/prefs.xml b/components/statusbar/content/prefs.xml deleted file mode 100644 index 44baab1..0000000 --- a/components/statusbar/content/prefs.xml +++ /dev/null @@ -1,704 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<!-- This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<!DOCTYPE bindings SYSTEM "chrome://browser/locale/statusbar/statusbar-prefs.dtd"> - -<bindings id="status4evar-prefs-bindings" - xmlns="http://www.mozilla.org/xbl" - xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:xbl="http://www.mozilla.org/xbl"> - - <binding id="css-bg-editor"> - <content sizetopopup="pref"> - <xul:vbox flex="1"> - <xul:deck anonid="css-bg-editor-deck" flex="1"> - <xul:vbox> - <xul:hbox align="center"> - <xul:label xbl:inherits="disabled">&status4evar.editor.css.color.label;</xul:label> - <xul:colorpicker anonid="css-bg-editor-color" type="button" onchange="this._editor._buildCSS();" xbl:inherits="disabled" /> - </xul:hbox> - - <xul:hbox align="center"> - <xul:label xbl:inherits="disabled">&status4evar.editor.css.image.label;</xul:label> - <xul:textbox anonid="css-bg-editor-image" readonly="true" flex="1" xbl:inherits="disabled" /> - <xul:button anonid="css-bg-editor-image-browse" label="&status4evar.option.browse;" oncommand="this._editor._imageBrowse();" xbl:inherits="disabled" /> - </xul:hbox> - <xul:hbox align="center" pack="end"> - <xul:button anonid="css-bg-editor-image-clear" label="&status4evar.option.clear;" oncommand="this._editor._imageClear();" xbl:inherits="disabled=no-image" /> - </xul:hbox> - - <xul:hbox> - <xul:groupbox pack="center"> - <xul:caption label="" /> - <xul:hbox flex="1" align="center"> - <xul:label>X</xul:label> - </xul:hbox> - <xul:separator class="groove" orient="horizontal" /> - <xul:hbox flex="1" align="center"> - <xul:label>Y</xul:label> - </xul:hbox> - </xul:groupbox> - - <xul:groupbox> - <xul:caption label="&status4evar.editor.css.image.repeat;" xbl:inherits="disabled=no-image" /> - <xul:menulist anonid="css-bg-editor-image-repeat-x" sizetopopup="always" onselect="this._editor._buildCSS();" xbl:inherits="disabled=no-image"> - <xul:menupopup> - <xul:menuitem label="&status4evar.option.no-repeat;" value="no-repeat" /> - <xul:menuitem label="&status4evar.option.repeat;" value="repeat" /> -<!-- - <xul:menuitem label="&status4evar.option.space;" value="space" /> - <xul:menuitem label="&status4evar.option.round;" value="round" /> ---> - </xul:menupopup> - </xul:menulist> - <xul:separator class="groove" orient="horizontal" /> - <xul:menulist anonid="css-bg-editor-image-repeat-y" sizetopopup="always" onselect="this._editor._buildCSS();" xbl:inherits="disabled=no-image"> - <xul:menupopup> - <xul:menuitem label="&status4evar.option.no-repeat;" value="no-repeat" /> - <xul:menuitem label="&status4evar.option.repeat;" value="repeat" /> -<!-- - <xul:menuitem label="&status4evar.option.space;" value="space" /> - <xul:menuitem label="&status4evar.option.round;" value="round" /> ---> - </xul:menupopup> - </xul:menulist> - </xul:groupbox> - - <xul:groupbox> - <xul:caption label="&status4evar.editor.css.image.position;" xbl:inherits="disabled=no-image" /> - <xul:menulist anonid="css-bg-editor-image-position-x" sizetopopup="always" onselect="this._editor._updatePositionX();" xbl:inherits="disabled=no-image"> - <xul:menupopup> - <xul:menuitem label="&status4evar.option.left;" value="left" /> - <xul:menuitem label="&status4evar.option.center;" value="center" /> - <xul:menuitem label="&status4evar.option.right;" value="right" /> - <xul:menuitem label="&status4evar.option.offset;" value="offset" /> - </xul:menupopup> - </xul:menulist> - <xul:separator class="groove" orient="horizontal" /> - <xul:menulist anonid="css-bg-editor-image-position-y" sizetopopup="always" onselect="this._editor._updatePositionY();" xbl:inherits="disabled=no-image"> - <xul:menupopup> - <xul:menuitem label="&status4evar.option.top;" value="top" /> - <xul:menuitem label="&status4evar.option.center;" value="center" /> - <xul:menuitem label="&status4evar.option.bottom;" value="bottom" /> - <xul:menuitem label="&status4evar.option.offset;" value="offset" /> - </xul:menupopup> - </xul:menulist> - </xul:groupbox> - - <xul:groupbox> - <xul:caption label="&status4evar.editor.css.image.offset;" xbl:inherits="disabled=no-image" /> - <xul:hbox> - <xul:textbox anonid="css-bg-editor-image-offset-x" type="number" size="4" min="-65535" onchange="this._editor._buildCSS();" /> - <xul:menulist anonid="css-bg-editor-image-offset-unit-x" sizetopopup="always" onselect="this._editor._buildCSS();"> - <xul:menupopup> - <xul:menuitem label="%" value="%" /> - <xul:menuitem label="px" value="px" /> - <xul:menuitem label="em" value="em" /> - <xul:menuitem label="in" value="in" /> - <xul:menuitem label="cm" value="cm" /> - <xul:menuitem label="mm" value="mm" /> - <xul:menuitem label="pt" value="pt" /> - <xul:menuitem label="pc" value="pc" /> - </xul:menupopup> - </xul:menulist> - </xul:hbox> - <xul:separator class="groove" orient="horizontal" /> - <xul:hbox> - <xul:textbox anonid="css-bg-editor-image-offset-y" type="number" size="4" min="-65535" onchange="this._editor._buildCSS();" /> - <xul:menulist anonid="css-bg-editor-image-offset-unit-y" sizetopopup="always" onselect="this._editor._buildCSS();"> - <xul:menupopup> - <xul:menuitem label="%" value="%" /> - <xul:menuitem label="px" value="px" /> - <xul:menuitem label="em" value="em" /> - <xul:menuitem label="in" value="in" /> - <xul:menuitem label="cm" value="cm" /> - <xul:menuitem label="mm" value="mm" /> - <xul:menuitem label="pt" value="pt" /> - <xul:menuitem label="pc" value="pc" /> - </xul:menupopup> - </xul:menulist> - </xul:hbox> - </xul:groupbox> - </xul:hbox> - </xul:vbox> - - <xul:textbox anonid="css-bg-editor-css-text" multiline="true" rows="6" xbl:inherits="disabled" /> - </xul:deck> - </xul:vbox> - - <xul:hbox align="center" pack="end"> - <children includes="progressmeter|toolbox" /> - <xul:label xbl:inherits="disabled">&status4evar.editor.label;</xul:label> - <xul:menulist anonid="css-bg-editor-mode-menu" sizetopopup="always" onselect="this._editor._updateMode();" xbl:inherits="disabled"> - <xul:menupopup> - <xul:menuitem label="&status4evar.option.simple;" /> - <xul:menuitem label="&status4evar.option.advanced;" /> - </xul:menupopup> - </xul:menulist> - </xul:hbox> - </content> - - <implementation> - <constructor><![CDATA[ - [ - "_editorColor", - "_editorImageBrowse", - "_editorImageClear", - "_editorImageRepeatX", - "_editorImageRepeatY", - "_editorImagePositionX", - "_editorImagePositionY", - "_editorImageOffsetX", - "_editorImageOffsetY", - "_editorImageOffsetUnitX", - "_editorImageOffsetUnitY", - "_editorMode" - ].forEach(function(prop) - { - this[prop]._editor = this; - }, this); - - this.setAdvanced(true, false); - ]]></constructor> - - <destructor><![CDATA[ - ]]></destructor> - - <field name="_disableBuildCSS"><![CDATA[ - true - ]]></field> - - <field name="_editorColor" readonly="true"><![CDATA[ - document.getAnonymousElementByAttribute(this, "anonid", "css-bg-editor-color"); - ]]></field> - - <field name="_editorCSS" readonly="true"><![CDATA[ - document.getAnonymousElementByAttribute(this, "anonid", "css-bg-editor-css-text"); - ]]></field> - - <field name="_editorDeck" readonly="true"><![CDATA[ - document.getAnonymousElementByAttribute(this, "anonid", "css-bg-editor-deck"); - ]]></field> - - <field name="_editorImage" readonly="true"><![CDATA[ - document.getAnonymousElementByAttribute(this, "anonid", "css-bg-editor-image"); - ]]></field> - - <field name="_editorImageBrowse" readonly="true"><![CDATA[ - document.getAnonymousElementByAttribute(this, "anonid", "css-bg-editor-image-browse"); - ]]></field> - - <field name="_editorImageClear" readonly="true"><![CDATA[ - document.getAnonymousElementByAttribute(this, "anonid", "css-bg-editor-image-clear"); - ]]></field> - - <field name="_editorImageRepeatX" readonly="true"><![CDATA[ - document.getAnonymousElementByAttribute(this, "anonid", "css-bg-editor-image-repeat-x"); - ]]></field> - - <field name="_editorImageRepeatY" readonly="true"><![CDATA[ - document.getAnonymousElementByAttribute(this, "anonid", "css-bg-editor-image-repeat-y"); - ]]></field> - - <field name="_editorImagePositionX" readonly="true"><![CDATA[ - document.getAnonymousElementByAttribute(this, "anonid", "css-bg-editor-image-position-x"); - ]]></field> - - <field name="_editorImagePositionY" readonly="true"><![CDATA[ - document.getAnonymousElementByAttribute(this, "anonid", "css-bg-editor-image-position-y"); - ]]></field> - - <field name="_editorImageOffsetX" readonly="true"><![CDATA[ - document.getAnonymousElementByAttribute(this, "anonid", "css-bg-editor-image-offset-x"); - ]]></field> - - <field name="_editorImageOffsetY" readonly="true"><![CDATA[ - document.getAnonymousElementByAttribute(this, "anonid", "css-bg-editor-image-offset-y"); - ]]></field> - - <field name="_editorImageOffsetUnitX" readonly="true"><![CDATA[ - document.getAnonymousElementByAttribute(this, "anonid", "css-bg-editor-image-offset-unit-x"); - ]]></field> - - <field name="_editorImageOffsetUnitY" readonly="true"><![CDATA[ - document.getAnonymousElementByAttribute(this, "anonid", "css-bg-editor-image-offset-unit-y"); - ]]></field> - - <field name="_editorMode" readonly="true"><![CDATA[ - document.getAnonymousElementByAttribute(this, "anonid", "css-bg-editor-mode-menu"); - ]]></field> - - <field name="_initialized"><![CDATA[ - false - ]]></field> - - <field name="_reRGB" readonly="true"><![CDATA[ - /^rgb\((\d+), (\d+), (\d+)\)$/ - ]]></field> - - <field name="_reURL" readonly="true"><![CDATA[ - /^url\(\s*['"]?(.+?)['"]?\s*\)$/ - ]]></field> - - <field name="_reBgPosition" readonly="true"><![CDATA[ - /^(left|center|right)? ?(-?\d+[^\s\d]+)? ?(top|center|bottom)? ?(-?\d+[^\s\d]+)?$/ - ]]></field> - - <field name="_reCSSUnit" readonly="true"><![CDATA[ - /^(-?\d+)([^\s\d]+)$/ - ]]></field> - - <field name="_strings" readonly="true"><![CDATA[ - document.getElementById("bundle_status4evar"); - ]]></field> - - <property name="value"> - <getter><![CDATA[ - return this._editorCSS.value; - ]]></getter> - <setter><![CDATA[ - this._editorCSS.value = val; - - if(!this._initialized) - { - this.setAdvanced(false, false); - this._initialized = true; - } - - return val; - ]]></setter> - </property> - - <property name="disabled"> - <getter><![CDATA[ - return this.getAttribute("disabled") == "true"; - ]]></getter> - <setter><![CDATA[ - if(val) - { - this.setAttribute("disabled", "true"); - } - else - { - this.removeAttribute("disabled"); - } - - this._updateImageControllDisable(); - - return val; - ]]></setter> - </property> - - <method name="setAdvanced"> - <parameter name="aVal"/> - <parameter name="aPrompt"/> - <body><![CDATA[ - if(!aVal) - { - let success = this._parseCSS(); - if(!success) - { - let result = aPrompt && Services.prompt.confirm(window, - this._strings.getString("simpleEditorTitle"), - this._strings.getString("simpleEditorMessage")); - if(result) - { // Continue to simple mode - this._buildCSS(); - } - else - { // Stay on advanced mode - aVal = true; - } - } - } - - this._disableBuildCSS = aVal; - this._editorDeck.selectedIndex = ((aVal) ? 1 : 0); - this._editorMode.selectedIndex = ((aVal) ? 1 : 0); - ]]></body> - </method> - - <method name="_buildCSS"> - <body><![CDATA[ - if(this._disableBuildCSS) - { - return; - } - - let cssVal = this._editorColor.color; - let imgVal = this._editorImage.value; - if(imgVal) - { - cssVal += " url(\"" + imgVal + "\")"; - - // - // Print the background repeat - // - let bgRX = this._editorImageRepeatX.value; - let bgRY = this._editorImageRepeatY.value; - if(bgRX == "repeat" && bgRY == "no-repeat") - { - cssVal += " repeat-x"; - } - else if(bgRX == "no-repeat" && bgRY == "repeat") - { - cssVal += " repeat-y"; - } - else - { - cssVal += " " + bgRX; - if(bgRX != bgRY) - { - cssVal += " " + bgRY; - } - } - - // - // Print the background position - // - let bgPX = this._editorImagePositionX.value; - let bgPOX = this._editorImageOffsetX.value; - if(bgPX != "offset") - { - cssVal += " " + bgPX; - } - else - { - cssVal += " " + bgPOX + this._editorImageOffsetUnitX.value; - } - - let bgPY = this._editorImagePositionY.value; - let bgPOY = this._editorImageOffsetY.value; - if(bgPY != "offset") - { - cssVal += " " + bgPY; - } - else - { - cssVal += " " + bgPOY + this._editorImageOffsetUnitY.value; - } - } - - this._editorCSS.value = cssVal; - - let event = document.createEvent("Event"); - event.initEvent("change", true, true); - this._editorCSS.dispatchEvent(event); - ]]></body> - </method> - - <method name="_parseCSS"> - <body><![CDATA[ - let retVal = true; - - let cssParser = document.createElement("div"); - cssParser.style.background = this._editorCSS.value; - if(!cssParser.style.background) - { - Components.utils.reportError("Error parsing background CSS rule: " + this._editorCSS.value); - cssParser.style.background = "#33FF33"; - retVal = false; - } - - // - // Parse the background color - // - let bgC = cssParser.style.backgroundColor; - if(this._reRGB.test(bgC)) - { - let digits = this._reRGB.exec(bgC); - - let red = parseInt(digits[1]); - let green = parseInt(digits[2]); - let blue = parseInt(digits[3]); - - let rgb = blue | (green << 8) | (red << 16); - bgC = "#" + rgb.toString(16); - } - else - { - Components.utils.reportError("Error parsing background-color value: " + bgC); - bgC = "#33FF33"; - retVal = false; - } - - // - // Parse the background image - // - let bgI = cssParser.style.backgroundImage; - if(bgI != "none" && !this._reURL.test(bgI)) - { - Components.utils.reportError("Error parsing background-image value: " + bgI); - bgI = "none"; - retVal = false; - } - bgI = ((bgI != "none") ? this._reURL.exec(bgI)[1].trim() : ""); - - // - // Parse the background repeat - // - let bgR = cssParser.style.backgroundRepeat.split(" "); - let bgRX = bgR[0]; - if(bgRX == "repeat-x") - { - bgRX = "repeat"; - } - else if(bgRX == "repeat-y") - { - bgRX = "no-repeat"; - } - - let bgRY = bgR[bgR.length - 1]; - if(bgRY == "repeat-x") - { - bgRY = "no-repeat"; - } - else if(bgRY == "repeat-y") - { - bgRY = "repeat"; - } - - // - // Parse the background position - // - let bgP = cssParser.style.backgroundPosition; - let bgPParts = this._reBgPosition.exec(bgP); - let bgPValues = new Array(); - for(let i = 1; i <= 4; i++) - { - if(bgPParts[i]) - { - bgPValues.push({ - "value": bgPParts[i], - "group": i - }); - } - } - - if(bgPValues.length == 1) - { - bgPValues.splice(((bgPValues[0].group == 2) ? 0 : 1), 0, { - "value": "center", - "group": ((bgPValues[0].group == 2) ? 0 : 2) - }); - } - - if(bgPValues.length == 2 && bgPValues[1].group == 2) - { - bgPValues[1].group = 4; - } - - for(let i = 0; i < 4; i++) - { - let group = (i + 1); - if(bgPValues[i] != undefined && bgPValues[i].group == group) - { - continue; - } - - let tmp = "0px"; - switch(i) - { - case 0: - tmp = "offset"; - break; - case 2: - tmp = "offset"; - break; - } - - bgPValues.splice(i, 0, { - "value": tmp, - "group": group - }); - } - - let bgPOXParts = this._reCSSUnit.exec(bgPValues[1].value); - let bgPOYParts = this._reCSSUnit.exec(bgPValues[3].value); - - // - // Parse the background size - // - - // - // Initialize the UI - // - let disableBuildCSS = this._disableBuildCSS; - this._disableBuildCSS = true; - - this._editorColor.color = bgC; - this._editorImage.value = bgI; - this._editorImageOffsetX.value = bgPOXParts[1]; - this._editorImageOffsetY.value = bgPOYParts[1]; - - [ - [this._editorImageRepeatX, bgRX, "repeat", "repeat X"], - [this._editorImageRepeatY, bgRY, "repeat", "repeat Y"], - [this._editorImagePositionX, bgPValues[0].value, "left", "position X"], - [this._editorImagePositionY, bgPValues[2].value, "top", "position Y"], - [this._editorImageOffsetUnitX, bgPOXParts[2], "px", "offset X unit"], - [this._editorImageOffsetUnitY, bgPOYParts[2], "px", "offset Y unit"] - ].forEach(function(info) - { - if(!this._setSelectedItemSafe(info[0], info[1], info[2])) - { - Components.utils.reportError("Error setting " + info[3] + " to " + info[1]); - retVal = false; - } - }, this); - - this._updateImageControllDisable(); - - this._disableBuildCSS = disableBuildCSS; - - return retVal; - ]]></body> - </method> - - <method name="_imageBrowse"> - <body><![CDATA[ - let nsIFilePicker = Components.interfaces.nsIFilePicker; - let filePicker = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker); - filePicker.init(window, this._strings.getString("imageSelectTitle"), nsIFilePicker.modeOpen); - filePicker.appendFilters(nsIFilePicker.filterImages); - - let res = filePicker.show(); - if(res == nsIFilePicker.returnOK) - { - this._editorImage.value = Services.io.newFileURI(filePicker.file).spec; - this._updateImageControllDisable(); - this._buildCSS(); - } - ]]></body> - </method> - - <method name="_imageClear"> - <body><![CDATA[ - this._editorImage.value = ""; - this._editorImageRepeatX.value = "repeat"; - this._editorImageRepeatY.value = "repeat"; - this._editorImagePositionX.value = "left"; - this._editorImagePositionY.value = "top"; - this._editorImageOffsetX.value = 0; - this._editorImageOffsetY.value = 0; - this._editorImageOffsetUnitX.value = "px"; - this._editorImageOffsetUnitY.value = "px"; - this._updateImageControllDisable(); - this._buildCSS(); - ]]></body> - </method> - - <method name="_processEvent"> - <parameter name="event"/> - <body><![CDATA[ - if(!("css-bg-editor-css-text" == event.originalTarget.getAttribute("anonid") - || "css-bg-editor-css-text" == document.getBindingParent(event.originalTarget).getAttribute("anonid"))) - { - event.stopPropagation(); - } - - //Components.utils.reportError("Editor event " + event.type + " on " + event.originalTarget.tagName + "::" + event.originalTarget.getAttribute("anonid")); - ]]></body> - </method> - - <method name="_setSelectedItemSafe"> - <parameter name="aElement"/> - <parameter name="aValue"/> - <parameter name="aDefault"/> - <body><![CDATA[ - aElement.value = aValue; - if(!aElement.selectedItem || aElement.selectedItem.value != aValue) - { - aElement.value = aDefault; - return false; - } - return true; - ]]></body> - </method> - - <method name="_updateImageControllDisable"> - <body><![CDATA[ - if(this.disabled || !this._editorImage.value) - { - this.setAttribute("no-image", "true"); - this._updatePositionOffsetXDisabled(true); - this._updatePositionOffsetYDisabled(true); - } - else - { - this.removeAttribute("no-image"); - this._updatePositionOffsetXDisabled(false); - this._updatePositionOffsetYDisabled(false); - } - ]]></body> - </method> - - <method name="_updateMode"> - <body><![CDATA[ - if(this._editorMode.selectedIndex == this._editorDeck.selectedIndex) - { - return; - } - - this.setAdvanced(((this._editorMode.selectedIndex == 1) ? true : false), true); - ]]></body> - </method> - - <method name="_updatePositionOffsetXDisabled"> - <parameter name="aVal"/> - <body><![CDATA[ - let bgPX = this._editorImagePositionX.value; - let disableOffsetX = aVal || (bgPX != "offset");// || bgPX == "center"); - this._editorImageOffsetX.disabled = disableOffsetX; - this._editorImageOffsetUnitX.disabled = disableOffsetX; - ]]></body> - </method> - - <method name="_updatePositionOffsetYDisabled"> - <parameter name="aVal"/> - <body><![CDATA[ - let bgPY = this._editorImagePositionY.value; - var disableOffsetY = aVal || (bgPY != "offset");// || bgPY == "center"); - this._editorImageOffsetY.disabled = disableOffsetY; - this._editorImageOffsetUnitY.disabled = disableOffsetY; - ]]></body> - </method> - - <method name="_updatePositionX"> - <body><![CDATA[ - this._updatePositionOffsetXDisabled(false); - this._buildCSS(); - ]]></body> - </method> - - <method name="_updatePositionY"> - <body><![CDATA[ - this._updatePositionOffsetYDisabled(false); - this._buildCSS(); - ]]></body> - </method> - </implementation> - - <handlers> - <handler event="command"><![CDATA[ - this._processEvent(event); - ]]></handler> - - <handler event="change"><![CDATA[ - this._processEvent(event); - ]]></handler> - - <handler event="input"><![CDATA[ - this._processEvent(event); - ]]></handler> - - <handler event="select"><![CDATA[ - this._processEvent(event); - ]]></handler> - </handlers> - </binding> -</bindings> - diff --git a/components/statusbar/content/prefs.xul b/components/statusbar/content/prefs.xul deleted file mode 100644 index dd41582..0000000 --- a/components/statusbar/content/prefs.xul +++ /dev/null @@ -1,297 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<!-- This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<!DOCTYPE prefwindow [ - <!ENTITY % prefsDTD SYSTEM "chrome://browser/locale/statusbar/statusbar-prefs.dtd"> - %prefsDTD; -]> - -<?xml-stylesheet href="chrome://global/skin/config.css" type="text/css" ?> -<?xml-stylesheet href="chrome://browser/skin/browser.css" type="text/css" ?> - -<?xml-stylesheet href="chrome://browser/content/statusbar/overlay.css" type="text/css" ?> -<?xml-stylesheet href="chrome://browser/skin/statusbar/overlay.css" type="text/css" ?> -<?xml-stylesheet href="chrome://browser/skin/statusbar/dynamic.css" type="text/css" ?> - -<?xml-stylesheet href="chrome://browser/content/statusbar/prefs.css" type="text/css" ?> -<?xml-stylesheet href="chrome://browser/skin/statusbar/prefs.css" type="text/css" ?> - -<prefwindow id="status4evar-prefs" title="&status4evar.window.title;" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - onload="status4evarPrefs.onPrefWindowLoad();" onunload="status4evarPrefs.onPrefWindowUnLoad();"> - - <stringbundleset id="stringbundleset"> - <stringbundle id="bundle_status4evar" src="chrome://browser/locale/statusbar/prefs.properties" /> - </stringbundleset> - <script type="application/javascript" src="chrome://browser/content/statusbar/prefs.js" /> - - <prefpane id="status4evar-pane-status" label="&status4evar.pane.status;"> - <preferences> - <preference id="status4evar-pref-status" name="status4evar.status" type="int" /> - <preference id="status4evar-pref-status-default" name="status4evar.status.default" type="bool" /> - <preference id="status4evar-pref-status-network" name="status4evar.status.network" type="bool" - onchange="status4evarPrefs.statusNetworkChanged();" /> - <preference id="status4evar-pref-status-network-xhr" name="status4evar.status.network.xhr" type="bool" /> - <preference id="status4evar-pref-status-timeout" name="status4evar.status.timeout" type="int" - onchange="status4evarPrefs.statusTimeoutChanged();" /> - <preference id="status4evar-pref-status-linkOver" name="status4evar.status.linkOver" type="int" /> - <preference id="status4evar-pref-status-linkOver-delay-show" name="status4evar.status.linkOver.delay.show" type="int" /> - <preference id="status4evar-pref-status-linkOver-delay-hide" name="status4evar.status.linkOver.delay.hide" type="int" /> - <preference id="status4evar-pref-status-toolbar-maxLength" name="status4evar.status.toolbar.maxLength" type="int" - onchange="status4evarPrefs.textLengthChanged();" /> - <preference id="status4evar-pref-status-popup-invertMirror" name="status4evar.status.popup.invertMirror" type="bool" /> - <preference id="status4evar-pref-status-popup-mouseMirror" name="status4evar.status.popup.mouseMirror" type="bool" /> - <preference id="toolkit-pref-dom-status-change" name="dom.disable_window_status_change" type="bool" inverted="true" /> - </preferences> - - <commandset id="status4evar-commandset-status"> - <command id="status4evar-command-status-timeout" oncommand="status4evarPrefs.statusTimeoutToggle();" /> - <command id="status4evar-command-status-toolbar-maxLength" oncommand="status4evarPrefs.textLengthToggle();" /> - </commandset> - - <tabbox id="status4evar-tabbox-status" flex="1"> - <tabs id="status4evar-tabs-status"> - <tab id="status4evar-tab-status-general" label="&status4evar.tab.general;" /> - <tab id="status4evar-tab-status-toolbar" label="&status4evar.tab.toolbar;" /> - <tab id="status4evar-tab-status-popup" label="&status4evar.tab.popup;" /> - </tabs> - - <tabpanels id="status4evar-tabpanels-status" flex="1"> - <tabpanel id="status4evar-tabpanel-status-general" orient="vertical"> - <groupbox id="status4evar-status-general-status"> - <caption label="&status4evar.status.general.status.caption;" /> - - <hbox align="center"> - <label id="status4evar-status-label" control="status4evar-status-menu">&status4evar.status.label;</label> - <menulist id="status4evar-status-menu" preference="status4evar-pref-status" sizetopopup="always"> - <menupopup> - <menuitem label="&status4evar.option.none;" value="0" /> - <menuitem label="&status4evar.option.toolbar;" value="1" /> - <menuitem label="&status4evar.option.popup;" value="3" /> - </menupopup> - </menulist> - </hbox> - - <hbox align="center"> - <checkbox id="status4evar-status-timeout-check" label="&status4evar.status.timeout.label;" - command="status4evar-command-status-timeout" /> - <textbox id="status4evar-status-timeout-value" preference="status4evar-pref-status-timeout" type="number" size="4" - onsyncfrompreference="return status4evarPrefs.statusTimeoutSync();" /> - <label id="status4evar-status-timeout-unit">&status4evar.unit.seconds;</label> - </hbox> - - <checkbox id="status4evar-status-default-check" preference="status4evar-pref-status-default" label="&status4evar.status.default.label;" /> - - <checkbox id="status4evar-status-network-check" preference="status4evar-pref-status-network" label="&status4evar.status.network.label;" - onsyncfrompreference="return status4evarPrefs.statusNetworkSync();" /> - - <hbox align="center" class="indent"> - <checkbox id="status4evar-status-network-xhr-check" preference="status4evar-pref-status-network-xhr" label="&status4evar.status.network.xhr.label;" /> - </hbox> - - <checkbox id="toolkit-dom-status-change-check" preference="toolkit-pref-dom-status-change" label="&toolkit.dom.status.change.label;" /> - </groupbox> - - <groupbox id="status4evar-status-general-linkOver"> - <caption label="&status4evar.status.general.linkOver.caption;" /> - - <hbox align="center"> - <label id="status4evar-status-linkOver-label" control="status4evar-status-linkOver-menu">&status4evar.status.linkOver.label;</label> - <menulist id="status4evar-status-linkOver-menu" preference="status4evar-pref-status-linkOver" sizetopopup="always"> - <menupopup> - <menuitem label="&status4evar.option.none;" value="0" /> - <menuitem label="&status4evar.option.toolbar;" value="1" /> - <menuitem label="&status4evar.option.popup;" value="3" /> - </menupopup> - </menulist> - </hbox> - - <hbox align="center"> - <label id="status4evar-status-linkOver-delay-show-label" control="status4evar-status-linkOver-delay-show-value">&status4evar.status.linkOver.delay.show.label;</label> - <textbox id="status4evar-status-linkOver-delay-show-value" preference="status4evar-pref-status-linkOver-delay-show" type="number" size="5" /> - <label id="status4evar-status-linkOver-delay-show-unit">&status4evar.unit.milliseconds;</label> - </hbox> - - <hbox align="center"> - <label id="status4evar-status-linkOver-delay-hide-label" control="status4evar-status-linkOver-delay-hide-value">&status4evar.status.linkOver.delay.hide.label;</label> - <textbox id="status4evar-status-linkOver-delay-hide-value" preference="status4evar-pref-status-linkOver-delay-hide" type="number" size="5" /> - <label id="status4evar-status-linkOver-delay-hide-unit">&status4evar.unit.milliseconds;</label> - </hbox> - </groupbox> - - </tabpanel> - - <tabpanel id="status4evar-tabpanel-status-toolbar" orient="vertical"> - <hbox align="center"> - <checkbox id="status4evar-status-toolbar-maxLength-check" label="&status4evar.status.toolbar.maxLength.label;" - command="status4evar-command-status-toolbar-maxLength" /> - <textbox id="status4evar-status-toolbar-maxLength-value" preference="status4evar-pref-status-toolbar-maxLength" type="number" size="4" - onsyncfrompreference="return status4evarPrefs.textLengthSync();" /> - <label id="status4evar-status-toolbar-maxLength-unit">&status4evar.unit.px;</label> - </hbox> - </tabpanel> - - <tabpanel id="status4evar-tabpanel-status-popup" orient="vertical"> - <checkbox id="status4evar-status-popup-invertMirror-check" preference="status4evar-pref-status-popup-invertMirror" label="&status4evar.status.popup.invertMirror.label;" /> - - <checkbox id="status4evar-status-popup-mouseMirror-check" preference="status4evar-pref-status-popup-mouseMirror" label="&status4evar.status.popup.mouseMirror.label;" /> - </tabpanel> - - </tabpanels> - </tabbox> - </prefpane> - - <prefpane id="status4evar-pane-progress" label="&status4evar.pane.progress;"> - <preferences> - <preference id="status4evar-pref-progress-toolbar-force" name="status4evar.progress.toolbar.force" type="bool" /> - <preference id="status4evar-pref-progress-toolbar-style" name="status4evar.progress.toolbar.style" type="bool" - onchange="status4evarPrefs.progressToolbarStyleChanged();" /> - <preference id="status4evar-pref-progress-toolbar-css" name="status4evar.progress.toolbar.css" type="string" - onchange="status4evarPrefs.progressToolbarCSSChanged();" /> - </preferences> - - <commandset id="status4evar-commandset-status"> - </commandset> - - <checkbox id="status4evar-progress-toolbar-force-check" preference="status4evar-pref-progress-toolbar-force" label="&status4evar.progress.toolbar.force.label;" /> - - <checkbox id="status4evar-progress-toolbar-style-check" preference="status4evar-pref-progress-toolbar-style" label="&status4evar.progress.style.label;" - onsyncfrompreference="return status4evarPrefs.progressToolbarStyleSync();" /> - - <vbox class="css-bg-editor" preference="status4evar-pref-progress-toolbar-css" preference-editable="true" flex="1"> - <progressmeter id="status4evar-progress-bar" value="75" flex="1" /> - </vbox> - </prefpane> - - <prefpane id="status4evar-pane-download" label="&status4evar.pane.download;"> - <preferences> - <preference id="status4evar-pref-download-button-action" name="status4evar.download.button.action" type="int" /> - <preference id="status4evar-pref-download-color-active" name="status4evar.download.color.active" type="string" /> - <preference id="status4evar-pref-download-color-paused" name="status4evar.download.color.paused" type="string" /> - <preference id="status4evar-pref-download-force" name="status4evar.download.force" type="bool" /> - <preference id="status4evar-pref-download-label" name="status4evar.download.label" type="int" /> - <preference id="status4evar-pref-download-label-force" name="status4evar.download.label.force" type="bool" /> - <preference id="status4evar-pref-download-notify-animate" name="status4evar.download.notify.animate" type="bool" /> - <preference id="status4evar-pref-download-notify-timeout" name="status4evar.download.notify.timeout" type="int" /> - <preference id="status4evar-pref-download-progress" name="status4evar.download.progress" type="int" /> - <preference id="status4evar-pref-download-tooltip" name="status4evar.download.tooltip" type="int" /> - - <preference id="status4evar-pref-download-button-action-command" name="status4evar.download.button.action.command" type="string"/> - </preferences> - - <commandset id="status4evar-commandset-download"> - <command id="status4evar-command-download-progress" oncommand="status4evarPrefs.downloadProgressToggle();" /> - </commandset> - - <checkbox id="status4evar-download-force-check" preference="status4evar-pref-download-force" label="&status4evar.download.force.label;" /> - - <checkbox id="status4evar-download-label-force-check" preference="status4evar-pref-download-label-force" label="&status4evar.download.label.force.label;" /> - - <hbox align="center"> - <label id="status4evar-download-label-label" control="status4evar-download-label-menu">&status4evar.download.label.label;</label> - <menulist id="status4evar-download-label-menu" preference="status4evar-pref-download-label" sizetopopup="always"> - <menupopup> - <menuitem value="0" label="&status4evar.option.dlcount;" /> - <menuitem value="1" label="&status4evar.option.dltime;" /> - <menuitem value="2" label="&status4evar.option.both;" /> - </menupopup> - </menulist> - </hbox> - - <hbox align="center"> - <label id="status4evar-download-tooltip-label" control="status4evar-download-tooltip-menu">&status4evar.download.tooltip.label;</label> - <menulist id="status4evar-download-tooltip-menu" preference="status4evar-pref-download-tooltip" sizetopopup="always"> - <menupopup> - <menuitem value="0" label="&status4evar.option.dlcount;" /> - <menuitem value="1" label="&status4evar.option.dltime;" /> - <menuitem value="2" label="&status4evar.option.both;" /> - </menupopup> - </menulist> - </hbox> - - <hbox align="center"> - <label id="status4evar-download-button-action-label" control="status4evar-download-button-action-menu">&status4evar.download.button.action.label;</label> - <menulist id="status4evar-download-button-action-menu" preference="status4evar-pref-download-button-action" sizetopopup="always"> - <menupopup> - <menuitem value="0" label="&status4evar.option.nothing;" /> - <menuitem value="1" label="&status4evar.option.firefoxdefault;" /> - <menuitem value="2" label="&status4evar.option.download.library;" /> - <menuitem value="3" label="&status4evar.option.download.tab;" /> - <menuitem value="4" label="&status4evar.option.download.thirdparty;" id="status4evar-download-button-action-menu-thirdparty" /> - </menupopup> - </menulist> - </hbox> - - <hbox align="center"> - <label id="status4evar-download-notify-timeout-label" control="status4evar-download-notify-timeout-value">&status4evar.download.notify.timeout.label;</label> - <textbox id="status4evar-download-notify-timeout-value" preference="status4evar-pref-download-notify-timeout" type="number" size="3" /> - <label id="status4evar-download-notify-timeout-unit">&status4evar.unit.seconds;</label> - </hbox> - - <checkbox id="status4evar-download-notify-animate-check" preference="status4evar-pref-download-notify-animate" label="&status4evar.download.notify.animate.label;" /> - - <checkbox id="status4evar-download-progress-check" command="status4evar-command-download-progress" label="&status4evar.download.progress.label;" /> - - <vbox class="indent"> - <hbox align="center"> - <radiogroup id="status4evar-download-progress-radiogroup" preference="status4evar-pref-download-progress" - onsyncfrompreference="return status4evarPrefs.downloadProgressSync();"> - <radio value="1" label="&status4evar.download.progress.average.label;" /> - <radio value="2" label="&status4evar.download.progress.max.label;" /> - <radio value="3" label="&status4evar.download.progress.min.label;" /> - </radiogroup> - </hbox> - - <hbox align="center"> - <label id="status4evar-download-color-active-label" control="status4evar-download-color-active-picker">&status4evar.download.color.active.label;</label> - <colorpicker id="status4evar-download-color-active-picker" preference="status4evar-pref-download-color-active" type="button" /> - </hbox> - - <hbox align="center"> - <label id="status4evar-download-color-paused-label" control="status4evar-download-color-paused-picker">&status4evar.download.color.paused.label;</label> - <colorpicker id="status4evar-download-color-paused-picker" preference="status4evar-pref-download-color-paused" type="button" /> - </hbox> - </vbox> - </prefpane> - - <prefpane id="status4evar-pane-addonbar" label="&status4evar.pane.statusbar;"> - <preferences> - <preference id="status4evar-pref-addonbar-borderStyle" name="status4evar.addonbar.borderStyle" type="bool" /> - <preference id="status4evar-pref-addonbar-closeButton" name="status4evar.addonbar.closeButton" type="bool" /> - <preference id="status4evar-pref-addonbar-windowGripper" name="status4evar.addonbar.windowGripper" type="bool" /> - </preferences> - - <checkbox id="status4evar-addonbar-borderStyle-check" preference="status4evar-pref-addonbar-borderStyle" label="&status4evar.addonbar.borderStyle;" /> - - <checkbox id="status4evar-addonbar-closeButton-check" preference="status4evar-pref-addonbar-closeButton" label="&status4evar.addonbar.closeButton;" /> - - <checkbox id="status4evar-addonbar-windowGripper-check" preference="status4evar-pref-addonbar-windowGripper" label="&status4evar.addonbar.windowGripper;" /> - </prefpane> - - <prefpane id="status4evar-pane-advanced" label="&status4evar.pane.advanced;"> - <preferences> - <preference id="status4evar-pref-advanced-status-detectFullScreen" name="status4evar.advanced.status.detectFullScreen" type="bool" /> - <preference id="status4evar-pref-advanced-status-detectVideo" name="status4evar.advanced.status.detectVideo" type="bool" /> - <preference id="browser-pref-urlbar-trimming-enabled" name="browser.urlbar.trimURLs" type="bool" /> - </preferences> - - <vbox flex="1"> - <groupbox id="status4evar-status-urlbar-builtin"> - <caption label="&status4evar.status.urlbar.firefox.builtin.caption;" /> - - <checkbox id="browser-urlbar-trimming-enabled-ckeck" preference="browser-pref-urlbar-trimming-enabled" label="&browser.urlbar.trimming.enabled.label;" /> - </groupbox> - - <groupbox id="status4evar-advanced-status"> - <caption label="&status4evar.pane.status;" /> - - <checkbox id="status4evar-advanced-status-detectFullScreen-check" preference="status4evar-pref-advanced-status-detectFullScreen" label="&status4evar.advanced.status.detectFullScreen;" /> - <checkbox id="status4evar-advanced-status-detectVideo-check" preference="status4evar-pref-advanced-status-detectVideo" label="&status4evar.advanced.status.detectVideo;" /> - </groupbox> - </vbox> - </prefpane> -</prefwindow> - diff --git a/components/statusbar/content/tabbrowser.xml b/components/statusbar/content/tabbrowser.xml deleted file mode 100644 index 2f47577..0000000 --- a/components/statusbar/content/tabbrowser.xml +++ /dev/null @@ -1,218 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<!-- This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<bindings id="status4evar-bindings" - xmlns="http://www.mozilla.org/xbl" - xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:xbl="http://www.mozilla.org/xbl"> - - <binding id="statuspanel" display="xul:hbox" extends="chrome://browser/content/tabbrowser.xml#statuspanel"> - <implementation> - <!-- --> - <!-- Inverted mirror handling --> - <!-- --> - - <field name="_invertMirror"><![CDATA[ - false - ]]></field> - - <property name="invertMirror"> - <setter><![CDATA[ - this._invertMirror = val; - this.mirror = this._isMirrored; - return val; - ]]></setter> - <getter><![CDATA[ - return this._invertMirror; - ]]></getter> - </property> - - <!-- --> - <!-- Mouse mirror handling --> - <!-- --> - - <field name="_mouseMirror"><![CDATA[ - true - ]]></field> - - <field name="_mouseMirrorListen"><![CDATA[ - false - ]]></field> - - <property name="mouseMirror"> - <setter><![CDATA[ - this._mouseMirror = val; - this.setupMouseMirror(this.value); - return val; - ]]></setter> - <getter><![CDATA[ - return this._mouseMirror; - ]]></getter> - </property> - - <method name="setupMouseMirror"> - <parameter name="val"/> - <body><![CDATA[ - if(val && this._mouseMirror) - { - this._calcMouseTargetRect(); - if(!this._mouseMirrorListen) - { - MousePosTracker.addListener(this); - this._mouseMirrorListen = true; - } - } - else - { - this.mirror = false; - if(this._mouseMirrorListen) - { - MousePosTracker.removeListener(this); - this._mouseMirrorListen = false; - } - } - ]]></body> - </method> - - <method name="_calcMouseTargetRect"> - <body><![CDATA[ - let alignRight = false; - let isRTL = (getComputedStyle(document.documentElement).direction == "rtl"); - if((this._invertMirror && !isRTL) || (!this._invertMirror && isRTL)) - { - alignRight = true; - } - - let rect = this.getBoundingClientRect(); - this._mouseTargetRect = - { - top: rect.top, - bottom: rect.bottom, - left: ((alignRight) ? window.innerWidth - rect.width : 0), - right: ((alignRight) ? window.innerWidth : rect.width) - }; - ]]></body> - </method> - - <method name="onMouseEnter"> - <body><![CDATA[ - this.mirror = true; - ]]></body> - </method> - - <method name="onMouseLeave"> - <body><![CDATA[ - this.mirror = false; - ]]></body> - </method> - - <!-- --> - <!-- Mirror handling --> - <!-- --> - - <field name="_isMirrored"><![CDATA[ - false - ]]></field> - - <property name="mirror"> - <setter><![CDATA[ - this._isMirrored = val; - if(this._invertMirror) - { - val = !val; - } - - this.setBooleanAttr("mirror", val); - ]]></setter> - <getter><![CDATA[ - return this._isMirrored; - ]]></getter> - </property> - - <method name="_mirror"> - <body><![CDATA[ - this.mirror = !this._isMirrored; - ]]></body> - </method> - - <!-- --> - <!-- Value handling --> - <!-- --> - - <property name="label"> - <setter><![CDATA[ - if(window.caligon && window.caligon.status4evar) - { - window.caligon.status4evar.statusService.setStatusText(val); - } - return undefined; - ]]></setter> - <getter><![CDATA[ - if(window.caligon && window.caligon.status4evar) - { - return window.caligon.status4evar.statusService.getStatusText(); - } - return ""; - ]]></getter> - </property> - - <property name="value"> - <setter><![CDATA[ - this.setValue(val); - this.setupMouseMirror(val); - return val; - ]]></setter> - <getter><![CDATA[ - return ((this.hasAttribute("inactive")) ? "" : this.getAttribute("label")); - ]]></getter> - </property> - - <method name="setValue"> - <parameter name="val"/> - <body><![CDATA[ - if((this.getAttribute("type") || "").indexOf("network") > -1 && (this.getAttribute("previoustype") || "").indexOf("network") > -1) - { - this.style.minWidth = getComputedStyle(this).width; - } - else - { - this.style.minWidth = ""; - } - - if(val) - { - this.setAttribute("label", val); - this.setBooleanAttr("inactive", false); - } - else - { - this.setBooleanAttr("inactive", true); - } - ]]></body> - </method> - - <!-- --> - <!-- Helpers --> - <!-- --> - - <method name="setBooleanAttr"> - <parameter name="name"/> - <parameter name="val"/> - <body><![CDATA[ - if(val) - { - this.setAttribute(name, "true"); - } - else - { - this.removeAttribute(name); - } - ]]></body> - </method> - </implementation> - </binding> -</bindings> - diff --git a/components/statusbar/jar.mn b/components/statusbar/jar.mn deleted file mode 100644 index b5a8d09..0000000 --- a/components/statusbar/jar.mn +++ /dev/null @@ -1,15 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -browser.jar: -% overlay chrome://browser/content/browser.xul chrome://browser/content/statusbar/overlay.xul -% style chrome://global/content/customizeToolbar.xul chrome://browser/skin/statusbar/overlay.css - content/browser/statusbar/overlay.js (content/overlay.js) - content/browser/statusbar/prefs.js (content/prefs.js) - content/browser/statusbar/prefs.xml (content/prefs.xml) - content/browser/statusbar/tabbrowser.xml (content/tabbrowser.xml) - content/browser/statusbar/overlay.xul (content/overlay.xul) - content/browser/statusbar/prefs.xul (content/prefs.xul) - content/browser/statusbar/overlay.css (content/overlay.css) - content/browser/statusbar/prefs.css (content/prefs.css)
\ No newline at end of file diff --git a/components/statusbar/moz.build b/components/statusbar/moz.build deleted file mode 100644 index 0f7f597..0000000 --- a/components/statusbar/moz.build +++ /dev/null @@ -1,25 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -JAR_MANIFESTS += [ 'jar.mn' ] - -XPIDL_SOURCES += [ 'status4evar.idl' ] - -XPIDL_MODULE = 'status4evar' - -EXTRA_COMPONENTS += [ - 'status4evar.js', - 'status4evar.manifest', -] - -EXTRA_JS_MODULES.statusbar = [ - 'content-thunk.js', - 'Downloads.jsm', - 'Progress.jsm', - 'Status.jsm', - 'Status4Evar.jsm', - 'Toolbars.jsm', -]
\ No newline at end of file diff --git a/components/statusbar/status4evar.idl b/components/statusbar/status4evar.idl deleted file mode 100644 index 534dea3..0000000 --- a/components/statusbar/status4evar.idl +++ /dev/null @@ -1,57 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" - -interface nsIDOMWindow; - -[scriptable, uuid(33d0433d-07be-4dc4-87fd-954057310efd)] -interface nsIStatus4Evar : nsISupports -{ - readonly attribute boolean addonbarBorderStyle; - readonly attribute boolean addonbarCloseButton; - readonly attribute boolean addonbarLegacyShim; - readonly attribute boolean addonbarWindowGripper; - - readonly attribute boolean advancedStatusDetectFullScreen; - readonly attribute boolean advancedStatusDetectVideo; - - readonly attribute long downloadButtonAction; - readonly attribute ACString downloadButtonActionCommand; - readonly attribute ACString downloadColorActive; - readonly attribute ACString downloadColorPaused; - readonly attribute boolean downloadForce; - readonly attribute long downloadLabel; - readonly attribute boolean downloadLabelForce; - readonly attribute boolean downloadNotifyAnimate; - readonly attribute long downloadNotifyTimeout; - readonly attribute long downloadProgress; - readonly attribute long downloadTooltip; - - readonly attribute boolean firstRun; - readonly attribute boolean firstRunAustralis; - - readonly attribute ACString progressToolbarCSS; - readonly attribute boolean progressToolbarForce; - readonly attribute boolean progressToolbarStyle; - readonly attribute boolean progressToolbarStyleAdvanced; - - readonly attribute long status; - readonly attribute boolean statusDefault; - readonly attribute boolean statusNetwork; - readonly attribute boolean statusNetworkXHR; - readonly attribute long statusTimeout; - readonly attribute long statusLinkOver; - readonly attribute long statusLinkOverDelayShow; - readonly attribute long statusLinkOverDelayHide; - - readonly attribute long statusToolbarMaxLength; - - readonly attribute boolean statusToolbarInvertMirror; - readonly attribute boolean statusToolbarMouseMirror; - - void resetPrefs(); - void updateWindow(in nsIDOMWindow win); -}; - diff --git a/components/statusbar/status4evar.js b/components/statusbar/status4evar.js deleted file mode 100644 index 4aa2e3e..0000000 --- a/components/statusbar/status4evar.js +++ /dev/null @@ -1,695 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -// Component constants -const CC = Components.classes; -const CI = Components.interfaces; -const CU = Components.utils; - -CU.import("resource://gre/modules/XPCOMUtils.jsm"); -CU.import("resource://gre/modules/Services.jsm"); - -const CURRENT_MIGRATION = 8; - -function Status_4_Evar(){} - -Status_4_Evar.prototype = -{ - classID: Components.ID("{33d0433d-07be-4dc4-87fd-954057310efd}"), - QueryInterface: XPCOMUtils.generateQI([ - CI.nsISupportsWeakReference, - CI.nsIObserver, - CI.nsIStatus4Evar - ]), - - prefs: null, - - addonbarBorderStyle: false, - addonbarCloseButton: false, - addonbarWindowGripper: true, - - advancedStatusDetectFullScreen: true, - advancedStatusDetectVideo: true, - - downloadButtonAction: 1, - downloadButtonActionCommand: "", - downloadColorActive: null, - downloadColorPaused: null, - downloadForce: false, - downloadLabel: 0, - downloadLabelForce: true, - downloadNotifyAnimate: true, - downloadNotifyTimeout: 60000, - downloadProgress: 1, - downloadTooltip: 1, - - firstRun: true, - - progressToolbarCSS: null, - progressToolbarForce: false, - progressToolbarStyle: false, - - status: 1, - statusDefault: true, - statusNetwork: true, - statusTimeout: 10000, - statusLinkOver: 1, - statusLinkOverDelayShow: 70, - statusLinkOverDelayHide: 150, - - statusToolbarMaxLength: 0, - - statusToolbarInvertMirror: false, - statusToolbarMouseMirror: true, - - pref_registry: - { - "addonbar.borderStyle": - { - update: function() - { - this.addonbarBorderStyle = this.prefs.getBoolPref("addonbar.borderStyle"); - }, - updateWindow: function(win) - { - let browser_bottom_box = win.caligon.status4evar.getters.browserBottomBox; - if(browser_bottom_box) - { - this.setBoolElementAttribute(browser_bottom_box, "s4eboarder", this.addonbarBorderStyle); - } - } - }, - - "addonbar.closeButton": - { - update: function() - { - this.addonbarCloseButton = this.prefs.getBoolPref("addonbar.closeButton"); - }, - updateWindow: function(win) - { - let addonbar_close_button = win.caligon.status4evar.getters.addonbarCloseButton; - if(addonbar_close_button) - { - addonbar_close_button.hidden = !this.addonbarCloseButton; - } - } - }, - - "addonbar.windowGripper": - { - update: function() - { - this.addonbarWindowGripper = this.prefs.getBoolPref("addonbar.windowGripper"); - }, - updateWindow: function(win) - { - win.caligon.status4evar.toolbars.updateWindowGripper(true); - } - }, - - "advanced.status.detectFullScreen": - { - update: function() - { - this.advancedStatusDetectFullScreen = this.prefs.getBoolPref("advanced.status.detectFullScreen"); - } - }, - - "advanced.status.detectVideo": - { - update: function() - { - this.advancedStatusDetectVideo = this.prefs.getBoolPref("advanced.status.detectVideo"); - } - }, - - "download.button.action": - { - update: function() - { - this.downloadButtonAction = this.prefs.getIntPref("download.button.action"); - }, - updateWindow: function(win) - { - win.caligon.status4evar.downloadStatus.updateBinding(); - } - }, - - "download.button.action.command": - { - update: function() - { - this.downloadButtonActionCommand = this.prefs.getCharPref("download.button.action.command"); - } - }, - - "download.color.active": - { - update: function() - { - this.downloadColorActive = this.prefs.getCharPref("download.color.active"); - }, - updateDynamicStyle: function(sheet) - { - sheet.cssRules[2].style.backgroundColor = this.downloadColorActive; - } - }, - - "download.color.paused": - { - update: function() - { - this.downloadColorPaused = this.prefs.getCharPref("download.color.paused"); - }, - updateDynamicStyle: function(sheet) - { - sheet.cssRules[3].style.backgroundColor = this.downloadColorPaused; - } - }, - - "download.force": - { - update: function() - { - this.downloadForce = this.prefs.getBoolPref("download.force"); - }, - updateWindow: function(win) - { - let download_button = win.caligon.status4evar.getters.downloadButton; - if(download_button) - { - this.setBoolElementAttribute(download_button, "forcevisible", this.downloadForce); - } - - let download_notify_anchor = win.caligon.status4evar.getters.downloadNotifyAnchor; - this.setBoolElementAttribute(download_notify_anchor, "forcevisible", this.downloadForce); - } - }, - - "download.label": - { - update: function() - { - this.downloadLabel = this.prefs.getIntPref("download.label"); - }, - updateWindow: function(win) - { - win.caligon.status4evar.downloadStatus.updateButton(); - } - }, - - "download.label.force": - { - update: function() - { - this.downloadLabelForce = this.prefs.getBoolPref("download.label.force"); - }, - updateWindow: function(win) - { - let download_button = win.caligon.status4evar.getters.downloadButton; - if(download_button) - { - this.setBoolElementAttribute(download_button, "forcelabel", this.downloadLabelForce); - } - } - }, - - "download.notify.animate": - { - update: function() - { - this.downloadNotifyAnimate = this.prefs.getBoolPref("download.notify.animate"); - } - }, - - "download.notify.timeout": - { - update: function() - { - this.downloadNotifyTimeout = (this.prefs.getIntPref("download.notify.timeout") * 1000); - } - }, - - "download.progress": - { - update: function() - { - this.downloadProgress = this.prefs.getIntPref("download.progress"); - }, - updateWindow: function(win) - { - win.caligon.status4evar.downloadStatus.updateButton(); - } - }, - - "download.tooltip": - { - update: function() - { - this.downloadTooltip = this.prefs.getIntPref("download.tooltip"); - }, - updateWindow: function(win) - { - win.caligon.status4evar.downloadStatus.updateButton(); - } - }, - - "progress.toolbar.css": - { - update: function() - { - this.progressToolbarCSS = this.prefs.getCharPref("progress.toolbar.css"); - }, - updateDynamicStyle: function(sheet) - { - sheet.cssRules[1].style.background = this.progressToolbarCSS; - } - }, - - "progress.toolbar.force": - { - update: function() - { - this.progressToolbarForce = this.prefs.getBoolPref("progress.toolbar.force"); - }, - updateWindow: function(win) - { - let toolbar_progress = win.caligon.status4evar.getters.toolbarProgress; - if(toolbar_progress) - { - this.setBoolElementAttribute(toolbar_progress, "forcevisible", this.progressToolbarForce); - } - } - }, - - "progress.toolbar.style": - { - update: function() - { - this.progressToolbarStyle = this.prefs.getBoolPref("progress.toolbar.style"); - }, - updateWindow: function(win) - { - let toolbar_progress = win.caligon.status4evar.getters.toolbarProgress; - if(toolbar_progress) - { - this.setBoolElementAttribute(toolbar_progress, "s4estyle", this.progressToolbarStyle); - } - } - }, - - "status": - { - update: function() - { - this.status = this.prefs.getIntPref("status"); - }, - updateWindow: function(win) - { - win.caligon.status4evar.statusService.clearStatusField(); - win.caligon.status4evar.statusService.updateStatusField(true); - } - }, - - "status.default": - { - update: function() - { - this.statusDefault = this.prefs.getBoolPref("status.default"); - }, - updateWindow: function(win) - { - win.caligon.status4evar.statusService.buildTextOrder(); - win.caligon.status4evar.statusService.updateStatusField(true); - } - }, - - "status.linkOver": - { - update: function() - { - this.statusLinkOver = this.prefs.getIntPref("status.linkOver"); - } - }, - - "status.linkOver.delay.show": - { - update: function() - { - this.statusLinkOverDelayShow = this.prefs.getIntPref("status.linkOver.delay.show"); - } - }, - - "status.linkOver.delay.hide": - { - update: function() - { - this.statusLinkOverDelayHide = this.prefs.getIntPref("status.linkOver.delay.hide"); - } - }, - - "status.network": - { - update: function() - { - this.statusNetwork = this.prefs.getBoolPref("status.network"); - }, - updateWindow: function(win) - { - win.caligon.status4evar.statusService.buildTextOrder(); - } - }, - - "status.network.xhr": - { - update: function() - { - this.statusNetworkXHR = this.prefs.getBoolPref("status.network.xhr"); - }, - updateWindow: function(win) - { - win.caligon.status4evar.statusService.buildTextOrder(); - } - }, - - "status.timeout": - { - update: function() - { - this.statusTimeout = (this.prefs.getIntPref("status.timeout") * 1000); - }, - updateWindow: function(win) - { - win.caligon.status4evar.statusService.updateStatusField(true); - } - }, - - "status.toolbar.maxLength": - { - update: function() - { - this.statusToolbarMaxLength = this.prefs.getIntPref("status.toolbar.maxLength"); - }, - updateWindow: function(win) - { - let status_widget = win.caligon.status4evar.getters.statusWidget; - if(status_widget) - { - status_widget.maxWidth = (this.statusToolbarMaxLength || ""); - } - } - }, - - "status.popup.invertMirror": - { - update: function() - { - this.statusToolbarInvertMirror = this.prefs.getBoolPref("status.popup.invertMirror"); - }, - updateWindow: function(win) - { - let statusOverlay = win.caligon.status4evar.getters.statusOverlay; - if(statusOverlay) - { - statusOverlay.invertMirror = this.statusToolbarInvertMirror; - } - } - }, - - "status.popup.mouseMirror": - { - update: function() - { - this.statusToolbarMouseMirror = this.prefs.getBoolPref("status.popup.mouseMirror"); - }, - updateWindow: function(win) - { - let statusOverlay = win.caligon.status4evar.getters.statusOverlay; - if(statusOverlay) - { - statusOverlay.mouseMirror = this.statusToolbarMouseMirror; - } - } - } - - }, - - // nsIObserver - observe: function(subject, topic, data) - { - try - { - switch(topic) - { - case "profile-after-change": - this.startup(); - break; - case "quit-application": - this.shutdown(); - break; - case "nsPref:changed": - this.updatePref(data, true); - break; - } - } - catch(e) - { - CU.reportError(e); - } - }, - - startup: function() - { - this.prefs = Services.prefs.getBranch("status4evar.").QueryInterface(CI.nsIPrefBranch2); - - this.firstRun = this.prefs.getBoolPref("firstRun"); - if(this.firstRun) - { - this.prefs.setBoolPref("firstRun", false); - } - - this.migrate(); - - for(let pref in this.pref_registry) - { - let pro = this.pref_registry[pref]; - - pro.update = pro.update.bind(this); - if(pro.updateWindow) - { - pro.updateWindow = pro.updateWindow.bind(this); - } - if(pro.updateDynamicStyle) - { - pro.updateDynamicStyle = pro.updateDynamicStyle.bind(this); - } - - this.prefs.addObserver(pref, this, true); - - this.updatePref(pref, false); - } - - Services.obs.addObserver(this, "quit-application", true); - }, - - shutdown: function() - { - Services.obs.removeObserver(this, "quit-application"); - - for(let pref in this.pref_registry) - { - this.prefs.removeObserver(pref, this); - } - - this.prefs = null; - }, - - migrate: function() - { - if(!this.firstRun) - { - let migration = 0; - try - { - migration = this.prefs.getIntPref("migration"); - } - catch(e) {} - - switch(migration) - { - case 5: - this.migrateBoolPref("status.detectFullScreen", "advanced.status.detectFullScreen"); - case 6: - let oldDownloadAction = this.prefs.getIntPref("download.button.action"); - let newDownloadAction = 1; - switch(oldDownloadAction) - { - case 2: - newDownloadAction = 1; - break; - case 3: - newDownloadAction = 2; - break; - case 4: - newDownloadAction = 1; - break; - } - this.prefs.setIntPref("download.button.action", newDownloadAction); - case 7: - let progressLocation = this.prefs.getIntPref("status"); - if (progressLocation == 2) - this.prefs.setIntPref("status", 1); - let linkOverLocation = this.prefs.getIntPref("status.linkOver"); - if (linkOverLocation == 2) - this.prefs.setIntPref("status.linkOver", 1); - break; - case CURRENT_MIGRATION: - break; - } - } - - this.prefs.setIntPref("migration", CURRENT_MIGRATION); - }, - - migrateBoolPref: function(oldPref, newPref) - { - if(this.prefs.prefHasUserValue(oldPref)) - { - this.prefs.setBoolPref(newPref, this.prefs.getBoolPref(oldPref)); - this.prefs.clearUserPref(oldPref); - } - }, - - migrateIntPref: function(oldPref, newPref) - { - if(this.prefs.prefHasUserValue(oldPref)) - { - this.prefs.setIntPref(newPref, this.prefs.getIntPref(oldPref)); - this.prefs.clearUserPref(oldPref); - } - }, - - migrateCharPref: function(oldPref, newPref) - { - if(this.prefs.prefHasUserValue(oldPref)) - { - this.prefs.setCharPref(newPref, this.prefs.getCharPref(oldPref)); - this.prefs.clearUserPref(oldPref); - } - }, - - updatePref: function(pref, updateWindows) - { - if(!(pref in this.pref_registry)) - { - return; - } - let pro = this.pref_registry[pref]; - - pro.update(); - - if(updateWindows) - { - let windowsEnum = Services.wm.getEnumerator("navigator:browser"); - while(windowsEnum.hasMoreElements()) - { - this.updateWindow(windowsEnum.getNext(), pro); - } - } - - if(pro.alsoUpdate) - { - pro.alsoUpdate.forEach(function (alsoPref) - { - this.updatePref(alsoPref); - }, this); - } - }, - - // Updtate a browser window - updateWindow: function(win, pro) - { - if(!(win instanceof CI.nsIDOMWindow) - || !(win.document.documentElement.getAttribute("windowtype") == "navigator:browser")) - { - return; - } - - if(pro) - { - this.handlePro(win, pro); - } - else - { - for(let pref in this.pref_registry) - { - this.handlePro(win, this.pref_registry[pref]); - } - } - }, - - handlePro: function(win, pro) - { - if(pro.updateWindow) - { - pro.updateWindow(win); - } - - if(pro.updateDynamicStyle) - { - let styleSheets = win.document.styleSheets; - for(let i = 0; i < styleSheets.length; i++) - { - let styleSheet = styleSheets[i]; - if(styleSheet.href == "chrome://browser/skin/statusbar/dynamic.css") - { - pro.updateDynamicStyle(styleSheet); - break; - } - } - } - }, - - setBoolElementAttribute: function(elem, attr, val) - { - if(val) - { - elem.setAttribute(attr, "true"); - } - else - { - elem.removeAttribute(attr); - } - }, - - setStringElementAttribute: function(elem, attr, val) - { - if(val) - { - elem.setAttribute(attr, val); - } - else - { - elem.removeAttribute(attr); - } - }, - - resetPrefs: function() - { - let childPrefs = this.prefs.getChildList(""); - childPrefs.forEach(function(pref) - { - if(this.prefs.prefHasUserValue(pref)) - { - this.prefs.clearUserPref(pref); - } - }, this); - } -}; - -const NSGetFactory = XPCOMUtils.generateNSGetFactory([Status_4_Evar]); - diff --git a/components/statusbar/status4evar.manifest b/components/statusbar/status4evar.manifest deleted file mode 100644 index 4bcf697..0000000 --- a/components/statusbar/status4evar.manifest +++ /dev/null @@ -1,3 +0,0 @@ -component {33d0433d-07be-4dc4-87fd-954057310efd} status4evar.js -contract @caligonstudios.com/status4evar;1 {33d0433d-07be-4dc4-87fd-954057310efd} -category profile-after-change Status-4-Evar @caligonstudios.com/status4evar;1 diff --git a/components/sync/aboutSyncTabs-bindings.xml b/components/sync/aboutSyncTabs-bindings.xml deleted file mode 100644 index e610820..0000000 --- a/components/sync/aboutSyncTabs-bindings.xml +++ /dev/null @@ -1,46 +0,0 @@ -<?xml version="1.0"?> - -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<bindings id="tabBindings" - xmlns="http://www.mozilla.org/xbl" - xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:xbl="http://www.mozilla.org/xbl"> - - <binding id="tab-listing" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem"> - <content> - <xul:hbox flex="1"> - <xul:vbox pack="start"> - <xul:image class="tabIcon" - xbl:inherits="src=icon"/> - </xul:vbox> - <xul:vbox pack="start" flex="1"> - <xul:label xbl:inherits="value=title,selected" - crop="end" flex="1" class="title"/> - <xul:label xbl:inherits="value=url,selected" - crop="end" flex="1" class="url"/> - </xul:vbox> - </xul:hbox> - </content> - <handlers> - <handler event="dblclick" button="0"> - <![CDATA[ - RemoteTabViewer.openSelected(); - ]]> - </handler> - </handlers> - </binding> - - <binding id="client-listing" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem"> - <content> - <xul:hbox pack="start" align="center" onfocus="event.target.blur()" onselect="return false;"> - <xul:image/> - <xul:label xbl:inherits="value=clientName" - class="clientName" - crop="center" flex="1"/> - </xul:hbox> - </content> - </binding> -</bindings> diff --git a/components/sync/aboutSyncTabs.css b/components/sync/aboutSyncTabs.css deleted file mode 100644 index 5a35317..0000000 --- a/components/sync/aboutSyncTabs.css +++ /dev/null @@ -1,11 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -richlistitem[type="tab"] { - -moz-binding: url(chrome://browser/content/sync/aboutSyncTabs-bindings.xml#tab-listing); -} - -richlistitem[type="client"] { - -moz-binding: url(chrome://browser/content/sync/aboutSyncTabs-bindings.xml#client-listing); -} diff --git a/components/sync/aboutSyncTabs.js b/components/sync/aboutSyncTabs.js deleted file mode 100644 index 410494b..0000000 --- a/components/sync/aboutSyncTabs.js +++ /dev/null @@ -1,313 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var Cu = Components.utils; - -Cu.import("resource://services-common/utils.js"); -Cu.import("resource://services-sync/main.js"); -Cu.import("resource:///modules/PlacesUIUtils.jsm"); -Cu.import("resource://gre/modules/PlacesUtils.jsm", this); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -var RemoteTabViewer = { - _tabsList: null, - - init: function () { - Services.obs.addObserver(this, "weave:service:login:finish", false); - Services.obs.addObserver(this, "weave:engine:sync:finish", false); - - this._tabsList = document.getElementById("tabsList"); - - this.buildList(true); - }, - - uninit: function () { - Services.obs.removeObserver(this, "weave:service:login:finish"); - Services.obs.removeObserver(this, "weave:engine:sync:finish"); - }, - - createItem: function(attrs) { - let item = document.createElement("richlistitem"); - - // Copy the attributes from the argument into the item - for (let attr in attrs) { - item.setAttribute(attr, attrs[attr]); - } - - if (attrs["type"] == "tab") { - item.label = attrs.title != "" ? attrs.title : attrs.url; - } - - return item; - }, - - filterTabs: function(event) { - let val = event.target.value.toLowerCase(); - let numTabs = this._tabsList.getRowCount(); - let clientTabs = 0; - let currentClient = null; - - for (let i = 0; i < numTabs; i++) { - let item = this._tabsList.getItemAtIndex(i); - let hide = false; - if (item.getAttribute("type") == "tab") { - if (!item.getAttribute("url").toLowerCase().includes(val) && - !item.getAttribute("title").toLowerCase().includes(val)) { - hide = true; - } else { - clientTabs++; - } - } - else if (item.getAttribute("type") == "client") { - if (currentClient) { - if (clientTabs == 0) { - currentClient.hidden = true; - } - } - currentClient = item; - clientTabs = 0; - } - item.hidden = hide; - } - if (clientTabs == 0) { - currentClient.hidden = true; - } - }, - - openSelected: function() { - let items = this._tabsList.selectedItems; - let urls = []; - for (let i = 0;i < items.length;i++) { - if (items[i].getAttribute("type") == "tab") { - urls.push(items[i].getAttribute("url")); - let index = this._tabsList.getIndexOfItem(items[i]); - this._tabsList.removeItemAt(index); - } - } - if (urls.length) { - getTopWin().gBrowser.loadTabs(urls); - this._tabsList.clearSelection(); - } - }, - - bookmarkSingleTab: function() { - let item = this._tabsList.selectedItems[0]; - let uri = Weave.Utils.makeURI(item.getAttribute("url")); - let title = item.getAttribute("title"); - PlacesUIUtils.showBookmarkDialog({ action: "add" - , type: "bookmark" - , uri: uri - , title: title - , hiddenRows: [ "description" - , "location" - , "loadInSidebar" - , "keyword" ] - }, window.top); - }, - - bookmarkSelectedTabs: function() { - let items = this._tabsList.selectedItems; - let URIs = []; - for (let i = 0;i < items.length;i++) { - if (items[i].getAttribute("type") == "tab") { - let uri = Weave.Utils.makeURI(items[i].getAttribute("url")); - if (!uri) { - continue; - } - - URIs.push(uri); - } - } - if (URIs.length) { - PlacesUIUtils.showBookmarkDialog({ action: "add" - , type: "folder" - , URIList: URIs - , hiddenRows: [ "description" ] - }, window.top); - } - }, - - getIcon: function (iconUri, defaultIcon) { - try { - let iconURI = Weave.Utils.makeURI(iconUri); - return PlacesUtils.favicons.getFaviconLinkForIcon(iconURI).spec; - } catch (ex) { - // Do nothing. - } - - // Just give the provided default icon or the system's default. - return defaultIcon || PlacesUtils.favicons.defaultFavicon.spec; - }, - - _waitingForBuildList: false, - - _buildListRequested: false, - - buildList: function (force) { - if (this._waitingForBuildList) { - this._buildListRequested = true; - return; - } - - this._waitingForBuildList = true; - this._buildListRequested = false; - - this._clearTabList(); - - if (Weave.Service.isLoggedIn && this._refetchTabs(force)) { - this._generateWeaveTabList(); - } else { - //XXXzpao We should say something about not being logged in & not having data - // or tell the appropriate condition. (bug 583344) - } - - function complete() { - this._waitingForBuildList = false; - if (this._buildListRequested) { - CommonUtils.nextTick(this.buildList, this); - } - } - - complete(); - }, - - _clearTabList: function () { - let list = this._tabsList; - - // Clear out existing richlistitems - let count = list.getRowCount(); - if (count > 0) { - for (let i = count - 1; i >= 0; i--) { - list.removeItemAt(i); - } - } - }, - - _generateWeaveTabList: function () { - let engine = Weave.Service.engineManager.get("tabs"); - let list = this._tabsList; - - let seenURLs = new Set(); - let localURLs = engine.getOpenURLs(); - - for (let [guid, client] in Iterator(engine.getAllClients())) { - // Create the client node, but don't add it in-case we don't show any tabs - let appendClient = true; - - client.tabs.forEach(function({title, urlHistory, icon}) { - let url = urlHistory[0]; - if (!url || localURLs.has(url) || seenURLs.has(url)) { - return; - } - seenURLs.add(url); - - if (appendClient) { - let attrs = { - type: "client", - clientName: client.clientName, - class: Weave.Service.clientsEngine.isMobile(client.id) ? "mobile" : "desktop" - }; - let clientEnt = this.createItem(attrs); - list.appendChild(clientEnt); - appendClient = false; - clientEnt.disabled = true; - } - let attrs = { - type: "tab", - title: title || url, - url: url, - icon: this.getIcon(icon), - } - let tab = this.createItem(attrs); - list.appendChild(tab); - }, this); - } - }, - - adjustContextMenu: function(event) { - let mode = "all"; - switch (this._tabsList.selectedItems.length) { - case 0: - break; - case 1: - mode = "single" - break; - default: - mode = "multiple"; - break; - } - - let menu = document.getElementById("tabListContext"); - let el = menu.firstChild; - while (el) { - let showFor = el.getAttribute("showFor"); - if (showFor) { - el.hidden = showFor != mode && showFor != "all"; - } - - el = el.nextSibling; - } - }, - - _refetchTabs: function(force) { - if (!force) { - // Don't bother refetching tabs if we already did so recently - let lastFetch = 0; - try { - lastFetch = Services.prefs.getIntPref("services.sync.lastTabFetch"); - } - catch (e) { - /* Just use the default value of 0 */ - } - - let now = Math.floor(Date.now() / 1000); - if (now - lastFetch < 30) { - return false; - } - } - - // if Clients hasn't synced yet this session, we need to sync it as well. - if (Weave.Service.clientsEngine.lastSync == 0) { - Weave.Service.clientsEngine.sync(); - } - - // Force a sync only for the tabs engine - let engine = Weave.Service.engineManager.get("tabs"); - engine.lastModified = null; - engine.sync(); - Services.prefs.setIntPref("services.sync.lastTabFetch", - Math.floor(Date.now() / 1000)); - - return true; - }, - - observe: function(subject, topic, data) { - switch (topic) { - case "weave:service:login:finish": - this.buildList(true); - break; - case "weave:engine:sync:finish": - if (subject == "tabs") { - this.buildList(false); - } - break; - } - }, - - handleClick: function(event) { - if (event.target.getAttribute("type") != "tab") { - return; - } - - - if (event.button == 1) { - let url = event.target.getAttribute("url"); - openUILink(url, event); - let index = this._tabsList.getIndexOfItem(event.target); - this._tabsList.removeItemAt(index); - } - } -} - diff --git a/components/sync/aboutSyncTabs.xul b/components/sync/aboutSyncTabs.xul deleted file mode 100644 index a4aa003..0000000 --- a/components/sync/aboutSyncTabs.xul +++ /dev/null @@ -1,68 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?> -<?xml-stylesheet href="chrome://browser/skin/aboutSyncTabs.css" type="text/css"?> -<?xml-stylesheet href="chrome://browser/content/sync/aboutSyncTabs.css" type="text/css"?> - -<!DOCTYPE window [ - <!ENTITY % aboutSyncTabsDTD SYSTEM "chrome://browser/locale/aboutSyncTabs.dtd"> - %aboutSyncTabsDTD; -]> - -<window id="tabs-display" - onload="RemoteTabViewer.init()" - onunload="RemoteTabViewer.uninit()" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:html="http://www.w3.org/1999/xhtml" - title="&tabs.otherDevices.label;"> - <script type="application/javascript;version=1.8" src="chrome://browser/content/sync/aboutSyncTabs.js"/> - <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/> - <html:head> - <html:link rel="icon" href="chrome://browser/skin/sync-16.png"/> - </html:head> - - <popupset id="contextmenus"> - <menupopup id="tabListContext"> - <menuitem label="&tabs.context.openTab.label;" - accesskey="&tabs.context.openTab.accesskey;" - oncommand="RemoteTabViewer.openSelected()" - showFor="single"/> - <menuitem label="&tabs.context.bookmarkSingleTab.label;" - accesskey="&tabs.context.bookmarkSingleTab.accesskey;" - oncommand="RemoteTabViewer.bookmarkSingleTab(event)" - showFor="single"/> - <menuitem label="&tabs.context.openMultipleTabs.label;" - accesskey="&tabs.context.openMultipleTabs.accesskey;" - oncommand="RemoteTabViewer.openSelected()" - showFor="multiple"/> - <menuitem label="&tabs.context.bookmarkMultipleTabs.label;" - accesskey="&tabs.context.bookmarkMultipleTabs.accesskey;" - oncommand="RemoteTabViewer.bookmarkSelectedTabs()" - showFor="multiple"/> - <menuseparator/> - <menuitem label="&tabs.context.refreshList.label;" - accesskey="&tabs.context.refreshList.accesskey;" - oncommand="RemoteTabViewer.buildList()" - showFor="all"/> - </menupopup> - </popupset> - <richlistbox context="tabListContext" id="tabsList" seltype="multiple" - align="center" flex="1" - onclick="RemoteTabViewer.handleClick(event)" - oncontextmenu="RemoteTabViewer.adjustContextMenu(event)"> - <hbox id="headers" align="center"> - <label id="tabsListHeading" - value="&tabs.otherDevices.label;"/> - <spacer flex="1"/> - <textbox type="search" - emptytext="&tabs.searchText.label;" - oncommand="RemoteTabViewer.filterTabs(event)"/> - </hbox> - - </richlistbox> -</window> - diff --git a/components/sync/addDevice.js b/components/sync/addDevice.js deleted file mode 100644 index 0390d43..0000000 --- a/components/sync/addDevice.js +++ /dev/null @@ -1,157 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var Ci = Components.interfaces; -var Cc = Components.classes; -var Cu = Components.utils; - -Cu.import("resource://services-sync/main.js"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -const PIN_PART_LENGTH = 4; - -const ADD_DEVICE_PAGE = 0; -const SYNC_KEY_PAGE = 1; -const DEVICE_CONNECTED_PAGE = 2; - -var gSyncAddDevice = { - - init: function init() { - this.pin1.setAttribute("maxlength", PIN_PART_LENGTH); - this.pin2.setAttribute("maxlength", PIN_PART_LENGTH); - this.pin3.setAttribute("maxlength", PIN_PART_LENGTH); - - this.nextFocusEl = {pin1: this.pin2, - pin2: this.pin3, - pin3: this.wizard.getButton("next")}; - - this.throbber = document.getElementById("pairDeviceThrobber"); - this.errorRow = document.getElementById("errorRow"); - - // Kick off a sync. That way the server will have the most recent data from - // this computer and it will show up immediately on the new device. - Weave.Service.scheduler.scheduleNextSync(0); - }, - - onPageShow: function onPageShow() { - this.wizard.getButton("back").hidden = true; - - switch (this.wizard.pageIndex) { - case ADD_DEVICE_PAGE: - this.onTextBoxInput(); - this.wizard.canRewind = false; - this.wizard.getButton("next").hidden = false; - this.pin1.focus(); - break; - case SYNC_KEY_PAGE: - this.wizard.canAdvance = false; - this.wizard.canRewind = true; - this.wizard.getButton("back").hidden = false; - this.wizard.getButton("next").hidden = true; - document.getElementById("weavePassphrase").value = - Weave.Utils.hyphenatePassphrase(Weave.Service.identity.syncKey); - break; - case DEVICE_CONNECTED_PAGE: - this.wizard.canAdvance = true; - this.wizard.canRewind = false; - this.wizard.getButton("cancel").hidden = true; - break; - } - }, - - onWizardAdvance: function onWizardAdvance() { - switch (this.wizard.pageIndex) { - case ADD_DEVICE_PAGE: - this.startTransfer(); - return false; - case DEVICE_CONNECTED_PAGE: - window.close(); - return false; - } - return true; - }, - - startTransfer: function startTransfer() { - this.errorRow.hidden = true; - // When onAbort is called, Weave may already be gone. - const JPAKE_ERROR_USERABORT = Weave.JPAKE_ERROR_USERABORT; - - let self = this; - let jpakeclient = this._jpakeclient = new Weave.JPAKEClient({ - onPaired: function onPaired() { - let credentials = {account: Weave.Service.identity.account, - password: Weave.Service.identity.basicPassword, - synckey: Weave.Service.identity.syncKey, - serverURL: Weave.Service.serverURL}; - jpakeclient.sendAndComplete(credentials); - }, - onComplete: function onComplete() { - delete self._jpakeclient; - self.wizard.pageIndex = DEVICE_CONNECTED_PAGE; - - // Schedule a Sync for soonish to fetch the data uploaded by the - // device with which we just paired. - Weave.Service.scheduler.scheduleNextSync(Weave.Service.scheduler.activeInterval); - }, - onAbort: function onAbort(error) { - delete self._jpakeclient; - - // Aborted by user, ignore. - if (error == JPAKE_ERROR_USERABORT) { - return; - } - - self.errorRow.hidden = false; - self.throbber.hidden = true; - self.pin1.value = self.pin2.value = self.pin3.value = ""; - self.pin1.disabled = self.pin2.disabled = self.pin3.disabled = false; - self.pin1.focus(); - } - }); - this.throbber.hidden = false; - this.pin1.disabled = this.pin2.disabled = this.pin3.disabled = true; - this.wizard.canAdvance = false; - - let pin = this.pin1.value + this.pin2.value + this.pin3.value; - let expectDelay = false; - jpakeclient.pairWithPIN(pin, expectDelay); - }, - - onWizardBack: function onWizardBack() { - if (this.wizard.pageIndex != SYNC_KEY_PAGE) - return true; - - this.wizard.pageIndex = ADD_DEVICE_PAGE; - return false; - }, - - onWizardCancel: function onWizardCancel() { - if (this._jpakeclient) { - this._jpakeclient.abort(); - delete this._jpakeclient; - } - return true; - }, - - onTextBoxInput: function onTextBoxInput(textbox) { - if (textbox && textbox.value.length == PIN_PART_LENGTH) - this.nextFocusEl[textbox.id].focus(); - - this.wizard.canAdvance = (this.pin1.value.length == PIN_PART_LENGTH - && this.pin2.value.length == PIN_PART_LENGTH - && this.pin3.value.length == PIN_PART_LENGTH); - }, - - goToSyncKeyPage: function goToSyncKeyPage() { - this.wizard.pageIndex = SYNC_KEY_PAGE; - } - -}; -// onWizardAdvance() and onPageShow() are run before init() so we'll set -// these up as lazy getters. -["wizard", "pin1", "pin2", "pin3"].forEach(function (id) { - XPCOMUtils.defineLazyGetter(gSyncAddDevice, id, function() { - return document.getElementById(id); - }); -}); diff --git a/components/sync/addDevice.xul b/components/sync/addDevice.xul deleted file mode 100644 index f2371aa..0000000 --- a/components/sync/addDevice.xul +++ /dev/null @@ -1,129 +0,0 @@ -<?xml version="1.0"?> - -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> -<?xml-stylesheet href="chrome://browser/skin/syncSetup.css" type="text/css"?> -<?xml-stylesheet href="chrome://browser/skin/syncCommon.css" type="text/css"?> - -<!DOCTYPE window [ -<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> -<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd"> -<!ENTITY % syncSetupDTD SYSTEM "chrome://browser/locale/syncSetup.dtd"> -%brandDTD; -%syncBrandDTD; -%syncSetupDTD; -]> -<wizard xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:html="http://www.w3.org/1999/xhtml" - id="wizard" - title="&pairDevice.title.label;" - windowtype="Sync:AddDevice" - persist="screenX screenY" - onwizardnext="return gSyncAddDevice.onWizardAdvance();" - onwizardback="return gSyncAddDevice.onWizardBack();" - onwizardcancel="gSyncAddDevice.onWizardCancel();" - onload="gSyncAddDevice.init();"> - - <script type="application/javascript" - src="chrome://browser/content/sync/addDevice.js"/> - <script type="application/javascript" - src="chrome://browser/content/sync/utils.js"/> - <script type="application/javascript" - src="chrome://browser/content/utilityOverlay.js"/> - <script type="application/javascript" - src="chrome://global/content/printUtils.js"/> - - <wizardpage id="addDevicePage" - label="&pairDevice.title.label;" - onpageshow="gSyncAddDevice.onPageShow();"> - <description> - &pairDevice.dialog.description.label; - <label class="text-link" - value="&addDevice.showMeHow.label;" - href="http://www.palemoon.org/sync/help/easy-setup.shtml"/> - </description> - <separator class="groove-thin"/> - <description> - &addDevice.dialog.enterCode.label; - </description> - <separator class="groove-thin"/> - <vbox align="center"> - <textbox id="pin1" - class="pin" - oninput="gSyncAddDevice.onTextBoxInput(this);" - onfocus="this.select();" - /> - <textbox id="pin2" - class="pin" - oninput="gSyncAddDevice.onTextBoxInput(this);" - onfocus="this.select();" - /> - <textbox id="pin3" - class="pin" - oninput="gSyncAddDevice.onTextBoxInput(this);" - onfocus="this.select();" - /> - </vbox> - <separator class="groove-thin"/> - <vbox id="pairDeviceThrobber" align="center" hidden="true"> - <image/> - </vbox> - <hbox id="errorRow" pack="center" hidden="true"> - <image class="statusIcon" status="error"/> - <label class="status" - value="&addDevice.dialog.tryAgain.label;"/> - </hbox> - <spacer flex="3"/> - <label class="text-link" - value="&addDevice.dontHaveDevice.label;" - onclick="gSyncAddDevice.goToSyncKeyPage();"/> - </wizardpage> - - <!-- Need a non-empty label here, otherwise we get a default label on Mac --> - <wizardpage id="syncKeyPage" - label=" " - onpageshow="gSyncAddDevice.onPageShow();"> - <description> - &addDevice.dialog.recoveryKey.label; - </description> - <spacer/> - - <groupbox> - <label value="&recoveryKeyEntry.label;" - accesskey="&recoveryKeyEntry.accesskey;" - control="weavePassphrase"/> - <textbox id="weavePassphrase" - readonly="true"/> - </groupbox> - - <groupbox align="center"> - <description>&recoveryKeyBackup.description;</description> - <hbox> - <button id="printSyncKeyButton" - label="&button.syncKeyBackup.print.label;" - accesskey="&button.syncKeyBackup.print.accesskey;" - oncommand="gSyncUtils.passphrasePrint('weavePassphrase');"/> - <button id="saveSyncKeyButton" - label="&button.syncKeyBackup.save.label;" - accesskey="&button.syncKeyBackup.save.accesskey;" - oncommand="gSyncUtils.passphraseSave('weavePassphrase');"/> - </hbox> - </groupbox> - </wizardpage> - - <wizardpage id="deviceConnectedPage" - label="&addDevice.dialog.connected.label;" - onpageshow="gSyncAddDevice.onPageShow();"> - <vbox align="center"> - <image id="successPageIcon"/> - </vbox> - <separator/> - <description class="normal"> - &addDevice.dialog.successful.label; - </description> - </wizardpage> - -</wizard> diff --git a/components/sync/genericChange.js b/components/sync/genericChange.js deleted file mode 100644 index df66391..0000000 --- a/components/sync/genericChange.js +++ /dev/null @@ -1,234 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var Ci = Components.interfaces; -var Cc = Components.classes; - -Components.utils.import("resource://services-sync/main.js"); -Components.utils.import("resource://gre/modules/Services.jsm"); - -var Change = { - _dialog: null, - _dialogType: null, - _status: null, - _statusIcon: null, - _firstBox: null, - _secondBox: null, - - get _passphraseBox() { - delete this._passphraseBox; - return this._passphraseBox = document.getElementById("passphraseBox"); - }, - - get _currentPasswordInvalid() { - return Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED; - }, - - get _updatingPassphrase() { - return this._dialogType == "UpdatePassphrase"; - }, - - onLoad: function Change_onLoad() { - /* Load labels */ - let introText = document.getElementById("introText"); - let introText2 = document.getElementById("introText2"); - let warningText = document.getElementById("warningText"); - - // load some other elements & info from the window - this._dialog = document.getElementById("change-dialog"); - this._dialogType = window.arguments[0]; - this._duringSetup = window.arguments[1]; - this._status = document.getElementById("status"); - this._statusIcon = document.getElementById("statusIcon"); - this._statusRow = document.getElementById("statusRow"); - this._firstBox = document.getElementById("textBox1"); - this._secondBox = document.getElementById("textBox2"); - - this._dialog.getButton("finish").disabled = true; - this._dialog.getButton("back").hidden = true; - - this._stringBundle = - Services.strings.createBundle("chrome://browser/locale/syncGenericChange.properties"); - - switch (this._dialogType) { - case "UpdatePassphrase": - case "ResetPassphrase": - document.getElementById("textBox1Row").hidden = true; - document.getElementById("textBox2Row").hidden = true; - document.getElementById("passphraseLabel").value - = this._str("new.recoverykey.label"); - document.getElementById("passphraseSpacer").hidden = false; - - if (this._updatingPassphrase) { - document.getElementById("passphraseHelpBox").hidden = false; - document.title = this._str("new.recoverykey.title"); - introText.textContent = this._str("new.recoverykey.introText"); - this._dialog.getButton("finish").label - = this._str("new.recoverykey.acceptButton"); - } - else { - document.getElementById("generatePassphraseButton").hidden = false; - document.getElementById("passphraseBackupButtons").hidden = false; - let pp = Weave.Service.identity.syncKey; - if (Weave.Utils.isPassphrase(pp)) - pp = Weave.Utils.hyphenatePassphrase(pp); - this._passphraseBox.value = pp; - this._passphraseBox.focus(); - document.title = this._str("change.recoverykey.title"); - introText.textContent = this._str("change.synckey.introText2"); - warningText.textContent = this._str("change.recoverykey.warningText"); - this._dialog.getButton("finish").label - = this._str("change.recoverykey.acceptButton"); - if (this._duringSetup) { - this._dialog.getButton("finish").disabled = false; - } - } - break; - case "ChangePassword": - document.getElementById("passphraseRow").hidden = true; - let box1label = document.getElementById("textBox1Label"); - let box2label = document.getElementById("textBox2Label"); - box1label.value = this._str("new.password.label"); - - if (this._currentPasswordInvalid) { - document.title = this._str("new.password.title"); - introText.textContent = this._str("new.password.introText"); - this._dialog.getButton("finish").label - = this._str("new.password.acceptButton"); - document.getElementById("textBox2Row").hidden = true; - } - else { - document.title = this._str("change.password.title"); - box2label.value = this._str("new.password.confirm"); - introText.textContent = this._str("change.password3.introText"); - warningText.textContent = this._str("change.password.warningText"); - this._dialog.getButton("finish").label - = this._str("change.password.acceptButton"); - } - break; - } - document.getElementById("change-page") - .setAttribute("label", document.title); - }, - - _clearStatus: function _clearStatus() { - this._status.value = ""; - this._statusIcon.removeAttribute("status"); - }, - - _updateStatus: function Change__updateStatus(str, state) { - this._updateStatusWithString(this._str(str), state); - }, - - _updateStatusWithString: function Change__updateStatusWithString(string, state) { - this._statusRow.hidden = false; - this._status.value = string; - this._statusIcon.setAttribute("status", state); - - let error = state == "error"; - this._dialog.getButton("cancel").disabled = !error; - this._dialog.getButton("finish").disabled = !error; - document.getElementById("printSyncKeyButton").disabled = !error; - document.getElementById("saveSyncKeyButton").disabled = !error; - - if (state == "success") - window.setTimeout(window.close, 1500); - }, - - onDialogAccept: function() { - switch (this._dialogType) { - case "UpdatePassphrase": - case "ResetPassphrase": - return this.doChangePassphrase(); - break; - case "ChangePassword": - return this.doChangePassword(); - break; - } - }, - - doGeneratePassphrase: function () { - let passphrase = Weave.Utils.generatePassphrase(); - this._passphraseBox.value = Weave.Utils.hyphenatePassphrase(passphrase); - this._dialog.getButton("finish").disabled = false; - }, - - doChangePassphrase: function Change_doChangePassphrase() { - let pp = Weave.Utils.normalizePassphrase(this._passphraseBox.value); - if (this._updatingPassphrase) { - Weave.Service.identity.syncKey = pp; - if (Weave.Service.login()) { - this._updateStatus("change.recoverykey.success", "success"); - Weave.Service.persistLogin(); - Weave.Service.scheduler.delayedAutoConnect(0); - } - else { - this._updateStatus("new.passphrase.status.incorrect", "error"); - } - } - else { - this._updateStatus("change.recoverykey.label", "active"); - - if (Weave.Service.changePassphrase(pp)) - this._updateStatus("change.recoverykey.success", "success"); - else - this._updateStatus("change.recoverykey.error", "error"); - } - - return false; - }, - - doChangePassword: function Change_doChangePassword() { - if (this._currentPasswordInvalid) { - Weave.Service.identity.basicPassword = this._firstBox.value; - if (Weave.Service.login()) { - this._updateStatus("change.password.status.success", "success"); - Weave.Service.persistLogin(); - } - else { - this._updateStatus("new.password.status.incorrect", "error"); - } - } - else { - this._updateStatus("change.password.status.active", "active"); - - if (Weave.Service.changePassword(this._firstBox.value)) - this._updateStatus("change.password.status.success", "success"); - else - this._updateStatus("change.password.status.error", "error"); - } - - return false; - }, - - validate: function (event) { - let valid = false; - let errorString = ""; - - if (this._dialogType == "ChangePassword") { - if (this._currentPasswordInvalid) - [valid, errorString] = gSyncUtils.validatePassword(this._firstBox); - else - [valid, errorString] = gSyncUtils.validatePassword(this._firstBox, this._secondBox); - } - else { - //Pale Moon: Enforce minimum length of 8 for allowed custom passphrase - //and don't restrict it to "out of sync" situations only. People who - //go to this page generally know what they are doing ;) - valid = this._passphraseBox.value.length >= 8; - } - - if (errorString == "") - this._clearStatus(); - else - this._updateStatusWithString(errorString, "error"); - - this._statusRow.hidden = valid; - this._dialog.getButton("finish").disabled = !valid; - }, - - _str: function Change__string(str) { - return this._stringBundle.GetStringFromName(str); - } -}; diff --git a/components/sync/genericChange.xul b/components/sync/genericChange.xul deleted file mode 100644 index 3c0b2cd..0000000 --- a/components/sync/genericChange.xul +++ /dev/null @@ -1,123 +0,0 @@ -<?xml version="1.0"?> - -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> -<?xml-stylesheet href="chrome://browser/skin/syncSetup.css" type="text/css"?> -<?xml-stylesheet href="chrome://browser/skin/syncCommon.css" type="text/css"?> - -<!DOCTYPE window [ -<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> -<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd"> -<!ENTITY % syncSetupDTD SYSTEM "chrome://browser/locale/syncSetup.dtd"> -%brandDTD; -%syncBrandDTD; -%syncSetupDTD; -]> -<wizard xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:html="http://www.w3.org/1999/xhtml" - id="change-dialog" - windowtype="Weave:ChangeSomething" - persist="screenX screenY" - onwizardnext="Change.onLoad()" - onwizardfinish="return Change.onDialogAccept();"> - - <script type="application/javascript" - src="chrome://browser/content/sync/genericChange.js"/> - <script type="application/javascript" - src="chrome://browser/content/sync/utils.js"/> - <script type="application/javascript" - src="chrome://global/content/printUtils.js"/> - - <wizardpage id="change-page" - label=""> - - <description id="introText"> - </description> - - <separator class="thin"/> - - <groupbox> - <grid> - <columns> - <column align="right"/> - <column flex="3"/> - <column flex="1"/> - </columns> - <rows> - <row id="textBox1Row" align="center"> - <label id="textBox1Label" control="textBox1"/> - <textbox id="textBox1" type="password" oninput="Change.validate()"/> - <spacer/> - </row> - <row id="textBox2Row" align="center"> - <label id="textBox2Label" control="textBox2"/> - <textbox id="textBox2" type="password" oninput="Change.validate()"/> - <spacer/> - </row> - </rows> - </grid> - - <vbox id="passphraseRow"> - <hbox flex="1"> - <label id="passphraseLabel" control="passphraseBox"/> - <spacer flex="1"/> - <label id="generatePassphraseButton" - hidden="true" - value="&syncGenerateNewKey.label;" - class="text-link inline-link" - onclick="event.stopPropagation(); - Change.doGeneratePassphrase();"/> - </hbox> - <textbox id="passphraseBox" - flex="1" - onfocus="this.select()" - oninput="Change.validate()"/> - </vbox> - - <vbox id="feedback" pack="center"> - <hbox id="statusRow" align="center"> - <image id="statusIcon" class="statusIcon"/> - <label id="status" class="status" value=" "/> - </hbox> - </vbox> - </groupbox> - - <separator class="thin"/> - - <hbox id="passphraseBackupButtons" - hidden="true" - pack="center"> - <button id="printSyncKeyButton" - label="&button.syncKeyBackup.print.label;" - accesskey="&button.syncKeyBackup.print.accesskey;" - oncommand="gSyncUtils.passphrasePrint('passphraseBox');"/> - <button id="saveSyncKeyButton" - label="&button.syncKeyBackup.save.label;" - accesskey="&button.syncKeyBackup.save.accesskey;" - oncommand="gSyncUtils.passphraseSave('passphraseBox');"/> - </hbox> - - <vbox id="passphraseHelpBox" - hidden="true"> - <description> - &existingRecoveryKey.description; - <label class="text-link" - href="http://www.palemoon.org/sync/help/recoverykey.shtml"> - &addDevice.showMeHow.label; - </label> - </description> - </vbox> - - <spacer id="passphraseSpacer" - flex="1" - hidden="true"/> - - <description id="warningText" class="data"> - </description> - - <spacer flex="1"/> - </wizardpage> -</wizard> diff --git a/components/sync/jar.mn b/components/sync/jar.mn deleted file mode 100644 index 3782038..0000000 --- a/components/sync/jar.mn +++ /dev/null @@ -1,22 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -browser.jar: - content/browser/sync/aboutSyncTabs.xul - content/browser/sync/aboutSyncTabs.js - content/browser/sync/aboutSyncTabs.css - content/browser/sync/aboutSyncTabs-bindings.xml - content/browser/sync/setup.xul - content/browser/sync/addDevice.js - content/browser/sync/addDevice.xul - content/browser/sync/setup.js - content/browser/sync/genericChange.xul - content/browser/sync/genericChange.js - content/browser/sync/key.xhtml - content/browser/sync/notification.xml - content/browser/sync/quota.xul - content/browser/sync/quota.js - content/browser/sync/utils.js - content/browser/sync/progress.js - content/browser/sync/progress.xhtml
\ No newline at end of file diff --git a/components/sync/key.xhtml b/components/sync/key.xhtml deleted file mode 100644 index 92abf0e..0000000 --- a/components/sync/key.xhtml +++ /dev/null @@ -1,54 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<!DOCTYPE html [ - <!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd"> - %htmlDTD; - <!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd"> - %syncBrandDTD; - <!ENTITY % syncKeyDTD SYSTEM "chrome://browser/locale/syncKey.dtd"> - %syncKeyDTD; - <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd" > - %globalDTD; -]> -<html xmlns="http://www.w3.org/1999/xhtml"> -<head> - <title>&syncKey.page.title;</title> - <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> - <meta name="robots" content="noindex"/> - <style type="text/css"> - #synckey { font-size: 150% } - footer { font-size: 70% } - /* Bug 575675: Need to have an a:visited rule in a chrome document. */ - a:visited { color: purple; } - </style> -</head> - -<body dir="&locale.dir;"> -<h1>&syncKey.page.title;</h1> - -<p id="synckey" dir="ltr">SYNCKEY</p> - -<p>&syncKey.page.description2;</p> - -<div id="column1"> - <h2>&syncKey.keepItSecret.heading;</h2> - <p>&syncKey.keepItSecret.description;</p> -</div> - -<div id="column2"> - <h2>&syncKey.keepItSafe.heading;</h2> - <p><em>&syncKey.keepItSafe1.description;</em>&syncKey.keepItSafe2.description;<em>&syncKey.keepItSafe3.description;</em>&syncKey.keepItSafe4a.description;</p> -</div> - -<p>&syncKey.findOutMore1.label;<a href="http://www.palemoon.org/sync/">http://www.palemoon.org/sync/</a>&syncKey.findOutMore2.label;</p> - -<footer> - &syncKey.footer1.label;<a id="tosLink" href="termsURL">termsURL</a>&syncKey.footer2.label;<a id="ppLink" href="privacyURL">privacyURL</a>&syncKey.footer3.label; -</footer> - -</body> -</html> diff --git a/components/sync/moz.build b/components/sync/moz.build deleted file mode 100644 index 2d64d50..0000000 --- a/components/sync/moz.build +++ /dev/null @@ -1,8 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -JAR_MANIFESTS += ['jar.mn'] - diff --git a/components/sync/notification.xml b/components/sync/notification.xml deleted file mode 100644 index 8ac881e..0000000 --- a/components/sync/notification.xml +++ /dev/null @@ -1,129 +0,0 @@ -<?xml version="1.0"?> - -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<!DOCTYPE bindings [ -<!ENTITY % notificationDTD SYSTEM "chrome://global/locale/notification.dtd"> -%notificationDTD; -]> - -<bindings id="notificationBindings" - xmlns="http://www.mozilla.org/xbl" - xmlns:xbl="http://www.mozilla.org/xbl" - xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - - <binding id="notificationbox" extends="chrome://global/content/bindings/notification.xml#notificationbox"> - <content> - <xul:vbox xbl:inherits="hidden=notificationshidden"> - <xul:spacer/> - <children includes="notification"/> - </xul:vbox> - <children/> - </content> - - <implementation> - <constructor><![CDATA[ - let temp = {}; - Cu.import("resource://services-common/observers.js", temp); - temp.Observers.add("weave:notification:added", this.onNotificationAdded, this); - temp.Observers.add("weave:notification:removed", this.onNotificationRemoved, this); - - for each (var notification in Weave.Notifications.notifications) - this._appendNotification(notification); - ]]></constructor> - - <destructor><![CDATA[ - let temp = {}; - Cu.import("resource://services-common/observers.js", temp); - temp.Observers.remove("weave:notification:added", this.onNotificationAdded, this); - temp.Observers.remove("weave:notification:removed", this.onNotificationRemoved, this); - ]]></destructor> - - <method name="onNotificationAdded"> - <parameter name="subject"/> - <parameter name="data"/> - <body><![CDATA[ - this._appendNotification(subject); - ]]></body> - </method> - - <method name="onNotificationRemoved"> - <parameter name="subject"/> - <parameter name="data"/> - <body><![CDATA[ - // If the view of the notification hasn't been removed yet, remove it. - var notifications = this.allNotifications; - for each (var notification in notifications) { - if (notification.notification == subject) { - notification.close(); - break; - } - } - ]]></body> - </method> - - <method name="_appendNotification"> - <parameter name="notification"/> - <body><![CDATA[ - var node = this.appendNotification(notification.description, - notification.title, - notification.iconURL, - notification.priority, - notification.buttons); - node.notification = notification; - ]]></body> - </method> - - </implementation> - </binding> - - <binding id="notification" extends="chrome://global/content/bindings/notification.xml#notification"> - <content> - <xul:hbox class="notification-inner outset" flex="1" xbl:inherits="type"> - <xul:toolbarbutton ondblclick="event.stopPropagation();" - class="messageCloseButton close-icon tabbable" - xbl:inherits="hidden=hideclose" - tooltiptext="&closeNotification.tooltip;" - oncommand="document.getBindingParent(this).close()"/> - <xul:hbox anonid="details" align="center" flex="1"> - <xul:image anonid="messageImage" class="messageImage" xbl:inherits="src=image,type"/> - <xul:description anonid="messageText" class="messageText" xbl:inherits="xbl:text=label"/> - - <!-- The children are the buttons defined by the notification. --> - <xul:hbox oncommand="document.getBindingParent(this)._doButtonCommand(event);"> - <children/> - </xul:hbox> - </xul:hbox> - </xul:hbox> - </content> - <implementation> - <!-- Note: this used to be a field, but for some reason it kept getting - - reset to its default value for TabNotification elements. - - As a property, that doesn't happen, even though the property stores - - its value in a JS property |_notification| that is not defined - - in XBL as a field or property. Maybe this is wrong, but it works. - --> - <property name="notification" - onget="return this._notification" - onset="this._notification = val; return val;"/> - <method name="close"> - <body><![CDATA[ - Weave.Notifications.remove(this.notification); - - // We should be able to call the base class's close method here - // to remove the notification element from the notification box, - // but we can't because of bug 373652, so instead we copied its code - // and execute it below. - var control = this.control; - if (control) - control.removeNotification(this); - else - this.hidden = true; - ]]></body> - </method> - </implementation> - </binding> - -</bindings> diff --git a/components/sync/progress.js b/components/sync/progress.js deleted file mode 100644 index 101160f..0000000 --- a/components/sync/progress.js +++ /dev/null @@ -1,71 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://services-sync/main.js"); - -var gProgressBar; -var gCounter = 0; - -function onLoad(event) { - Services.obs.addObserver(onEngineSync, "weave:engine:sync:finish", false); - Services.obs.addObserver(onEngineSync, "weave:engine:sync:error", false); - Services.obs.addObserver(onServiceSync, "weave:service:sync:finish", false); - Services.obs.addObserver(onServiceSync, "weave:service:sync:error", false); - - gProgressBar = document.getElementById('uploadProgressBar'); - - if (Services.prefs.getPrefType("services.sync.firstSync") != Ci.nsIPrefBranch.PREF_INVALID) { - gProgressBar.hidden = false; - } - else { - gProgressBar.hidden = true; - } -} - -function onUnload(event) { - cleanUpObservers(); -} - -function cleanUpObservers() { - try { - Services.obs.removeObserver(onEngineSync, "weave:engine:sync:finish"); - Services.obs.removeObserver(onEngineSync, "weave:engine:sync:error"); - Services.obs.removeObserver(onServiceSync, "weave:service:sync:finish"); - Services.obs.removeObserver(onServiceSync, "weave:service:sync:error"); - } - catch (e) { - // may be double called by unload & exit. Ignore. - } -} - -function onEngineSync(subject, topic, data) { - // The Clients engine syncs first. At this point we don't necessarily know - // yet how many engines will be enabled, so we'll ignore the Clients engine - // and evaluate how many engines are enabled when the first "real" engine - // syncs. - if (data == "clients") { - return; - } - - if (!gCounter && - Services.prefs.getPrefType("services.sync.firstSync") != Ci.nsIPrefBranch.PREF_INVALID) { - gProgressBar.max = Weave.Service.engineManager.getEnabled().length; - } - - gCounter += 1; - gProgressBar.setAttribute("value", gCounter); -} - -function onServiceSync(subject, topic, data) { - // To address the case where 0 engines are synced, we will fill the - // progress bar so the user knows that the sync has finished. - gProgressBar.setAttribute("value", gProgressBar.max); - cleanUpObservers(); -} - -function closeTab() { - window.close(); -} diff --git a/components/sync/progress.xhtml b/components/sync/progress.xhtml deleted file mode 100644 index d403cb2..0000000 --- a/components/sync/progress.xhtml +++ /dev/null @@ -1,55 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<!DOCTYPE html [ - <!ENTITY % htmlDTD - PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" - "DTD/xhtml1-strict.dtd"> - %htmlDTD; - <!ENTITY % syncProgressDTD - SYSTEM "chrome://browser/locale/syncProgress.dtd"> - %syncProgressDTD; - <!ENTITY % syncSetupDTD - SYSTEM "chrome://browser/locale/syncSetup.dtd"> - %syncSetupDTD; - <!ENTITY % globalDTD - SYSTEM "chrome://global/locale/global.dtd"> - %globalDTD; -]> - -<html xmlns="http://www.w3.org/1999/xhtml"> - <head> - <title>&syncProgress.pageTitle;</title> - - <link rel="stylesheet" type="text/css" media="all" - href="chrome://browser/skin/syncProgress.css"/> - - <link rel="icon" type="image/png" id="favicon" - href="chrome://browser/skin/sync-16.png"/> - - <script type="text/javascript;version=1.8" - src="chrome://browser/content/sync/progress.js"/> - </head> - <body onload="onLoad(event)" onunload="onUnload(event)" dir="&locale.dir;"> - <title>&setup.successPage.title;</title> - <div id="floatingBox" class="main-content"> - <div id="title"> - <h1>&setup.successPage.title;</h1> - </div> - <div id="successLogo"> - <img id="brandSyncLogo" src="chrome://browser/skin/sync-128.png" alt="&syncProgress.logoAltText;" /> - </div> - <div id="loadingText"> - <p id="blurb">&syncProgress.textBlurb; </p> - </div> - <div id="progressBar"> - <progress id="uploadProgressBar" value="0"/> - </div> - <div id="bottomRow"> - <button id="closeButton" onclick="closeTab()">&syncProgress.closeButton; </button> - </div> - </div> - </body> -</html> diff --git a/components/sync/quota.js b/components/sync/quota.js deleted file mode 100644 index b416a44..0000000 --- a/components/sync/quota.js +++ /dev/null @@ -1,247 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -const Ci = Components.interfaces; -const Cc = Components.classes; -const Cr = Components.results; -const Cu = Components.utils; - -Cu.import("resource://services-sync/main.js"); -Cu.import("resource://gre/modules/DownloadUtils.jsm"); - -var gSyncQuota = { - - init: function init() { - this.bundle = document.getElementById("quotaStrings"); - let caption = document.getElementById("treeCaption"); - caption.firstChild.nodeValue = this.bundle.getString("quota.treeCaption.label"); - - gUsageTreeView.init(); - this.tree = document.getElementById("usageTree"); - this.tree.view = gUsageTreeView; - - this.loadData(); - }, - - loadData: function loadData() { - this._usage_req = Weave.Service.getStorageInfo(Weave.INFO_COLLECTION_USAGE, - function (error, usage) { - delete gSyncQuota._usage_req; - // displayUsageData handles null values, so no need to check 'error'. - gUsageTreeView.displayUsageData(usage); - }); - - let usageLabel = document.getElementById("usageLabel"); - let bundle = this.bundle; - - this._quota_req = Weave.Service.getStorageInfo(Weave.INFO_QUOTA, - function (error, quota) { - delete gSyncQuota._quota_req; - - if (error) { - usageLabel.value = bundle.getString("quota.usageError.label"); - return; - } - let used = gSyncQuota.convertKB(quota[0]); - if (!quota[1]) { - // No quota on the server. - usageLabel.value = bundle.getFormattedString( - "quota.usageNoQuota.label", used); - return; - } - let percent = Math.round(100 * quota[0] / quota[1]); - let total = gSyncQuota.convertKB(quota[1]); - usageLabel.value = bundle.getFormattedString( - "quota.usagePercentage.label", [percent].concat(used).concat(total)); - }); - }, - - onCancel: function onCancel() { - if (this._usage_req) { - this._usage_req.abort(); - } - if (this._quota_req) { - this._quota_req.abort(); - } - return true; - }, - - onAccept: function onAccept() { - let engines = gUsageTreeView.getEnginesToDisable(); - for each (let engine in engines) { - Weave.Service.engineManager.get(engine).enabled = false; - } - if (engines.length) { - // The 'Weave' object will disappear once the window closes. - let Service = Weave.Service; - Weave.Utils.nextTick(function() { Service.sync(); }); - } - return this.onCancel(); - }, - - convertKB: function convertKB(value) { - return DownloadUtils.convertByteUnits(value * 1024); - } - -}; - -var gUsageTreeView = { - - _ignored: {keys: true, - meta: true, - clients: true}, - - /* - * Internal data structures underlaying the tree. - */ - _collections: [], - _byname: {}, - - init: function init() { - let retrievingLabel = gSyncQuota.bundle.getString("quota.retrieving.label"); - for each (let engine in Weave.Service.engineManager.getEnabled()) { - if (this._ignored[engine.name]) - continue; - - // Some engines use the same pref, which means they can only be turned on - // and off together. We need to combine them here as well. - let existing = this._byname[engine.prefName]; - if (existing) { - existing.engines.push(engine.name); - continue; - } - - let obj = {name: engine.prefName, - title: this._collectionTitle(engine), - engines: [engine.name], - enabled: true, - sizeLabel: retrievingLabel}; - this._collections.push(obj); - this._byname[engine.prefName] = obj; - } - }, - - _collectionTitle: function _collectionTitle(engine) { - try { - return gSyncQuota.bundle.getString( - "collection." + engine.prefName + ".label"); - } catch (ex) { - return engine.Name; - } - }, - - /* - * Process the quota information as returned by info/collection_usage. - */ - displayUsageData: function displayUsageData(data) { - for each (let coll in this._collections) { - coll.size = 0; - // If we couldn't retrieve any data, just blank out the label. - if (!data) { - coll.sizeLabel = ""; - continue; - } - - for each (let engineName in coll.engines) - coll.size += data[engineName] || 0; - let sizeLabel = ""; - sizeLabel = gSyncQuota.bundle.getFormattedString( - "quota.sizeValueUnit.label", gSyncQuota.convertKB(coll.size)); - coll.sizeLabel = sizeLabel; - } - let sizeColumn = this.treeBox.columns.getNamedColumn("size"); - this.treeBox.invalidateColumn(sizeColumn); - }, - - /* - * Handle click events on the tree. - */ - onTreeClick: function onTreeClick(event) { - if (event.button == 2) - return; - - let cell = this.treeBox.getCellAt(event.clientX, event.clientY); - if (cell.col && cell.col.id == "enabled") - this.toggle(cell.row); - }, - - /* - * Toggle enabled state of an engine. - */ - toggle: function toggle(row) { - // Update the tree - let collection = this._collections[row]; - collection.enabled = !collection.enabled; - this.treeBox.invalidateRow(row); - }, - - /* - * Return a list of engines (or rather their pref names) that should be - * disabled. - */ - getEnginesToDisable: function getEnginesToDisable() { - // Tycho: return [coll.name for each (coll in this._collections) if (!coll.enabled)]; - let engines = []; - for each (let coll in this._collections) { - if (!coll.enabled) { - engines.push(coll.name); - } - } - return engines; - }, - - // nsITreeView - - get rowCount() { - return this._collections.length; - }, - - getRowProperties: function(index) { return ""; }, - getCellProperties: function(row, col) { return ""; }, - getColumnProperties: function(col) { return ""; }, - isContainer: function(index) { return false; }, - isContainerOpen: function(index) { return false; }, - isContainerEmpty: function(index) { return false; }, - isSeparator: function(index) { return false; }, - isSorted: function() { return false; }, - canDrop: function(index, orientation, dataTransfer) { return false; }, - drop: function(row, orientation, dataTransfer) {}, - getParentIndex: function(rowIndex) {}, - hasNextSibling: function(rowIndex, afterIndex) { return false; }, - getLevel: function(index) { return 0; }, - getImageSrc: function(row, col) {}, - - getCellValue: function(row, col) { - return this._collections[row].enabled; - }, - - getCellText: function getCellText(row, col) { - let collection = this._collections[row]; - switch (col.id) { - case "collection": - return collection.title; - case "size": - return collection.sizeLabel; - default: - return ""; - } - }, - - setTree: function setTree(tree) { - this.treeBox = tree; - }, - - toggleOpenState: function(index) {}, - cycleHeader: function(col) {}, - selectionChanged: function() {}, - cycleCell: function(row, col) {}, - isEditable: function(row, col) { return false; }, - isSelectable: function (row, col) { return false; }, - setCellValue: function(row, col, value) {}, - setCellText: function(row, col, value) {}, - performAction: function(action) {}, - performActionOnRow: function(action, row) {}, - performActionOnCell: function(action, row, col) {} - -}; diff --git a/components/sync/quota.xul b/components/sync/quota.xul deleted file mode 100644 index 99e6ed7..0000000 --- a/components/sync/quota.xul +++ /dev/null @@ -1,65 +0,0 @@ -<?xml version="1.0"?> - -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> -<?xml-stylesheet href="chrome://browser/skin/syncQuota.css"?> - -<!DOCTYPE dialog [ -<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> -<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd"> -<!ENTITY % syncQuotaDTD SYSTEM "chrome://browser/locale/syncQuota.dtd"> -%brandDTD; -%syncBrandDTD; -%syncQuotaDTD; -]> -<dialog id="quotaDialog" - windowtype="Sync:ViewQuota" - persist="screenX screenY width height" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:html="http://www.w3.org/1999/xhtml" - onload="gSyncQuota.init()" - buttons="accept,cancel" - title=""a.dialogTitle.label;" - ondialogcancel="return gSyncQuota.onCancel();" - ondialogaccept="return gSyncQuota.onAccept();"> - - <script type="application/javascript" - src="chrome://browser/content/sync/quota.js"/> - - <stringbundleset id="stringbundleset"> - <stringbundle id="quotaStrings" - src="chrome://browser/locale/syncQuota.properties"/> - </stringbundleset> - - <vbox flex="1"> - <label id="usageLabel" - value=""a.retrievingInfo.label;"/> - <separator/> - <tree id="usageTree" - seltype="single" - hidecolumnpicker="true" - onclick="gUsageTreeView.onTreeClick(event);" - flex="1"> - <treecols> - <treecol id="enabled" - type="checkbox" - fixed="true"/> - <splitter class="tree-splitter"/> - <treecol id="collection" - label=""a.typeColumn.label;" - flex="1"/> - <splitter class="tree-splitter"/> - <treecol id="size" - label=""a.sizeColumn.label;" - flex="1"/> - </treecols> - <treechildren flex="1"/> - </tree> - <separator/> - <description id="treeCaption"> </description> - </vbox> - -</dialog> diff --git a/components/sync/setup.js b/components/sync/setup.js deleted file mode 100644 index e8d67a5..0000000 --- a/components/sync/setup.js +++ /dev/null @@ -1,1071 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var Ci = Components.interfaces; -var Cc = Components.classes; -var Cr = Components.results; -var Cu = Components.utils; - -// page consts - -const PAIR_PAGE = 0; -const INTRO_PAGE = 1; -const NEW_ACCOUNT_START_PAGE = 2; -const EXISTING_ACCOUNT_CONNECT_PAGE = 3; -const EXISTING_ACCOUNT_LOGIN_PAGE = 4; -const OPTIONS_PAGE = 5; -const OPTIONS_CONFIRM_PAGE = 6; - -// Broader than we'd like, but after this changed from api-secure.recaptcha.net -// we had no choice. At least we only do this for the duration of setup. -// See discussion in Bugs 508112 and 653307. -const RECAPTCHA_DOMAIN = "https://www.google.com"; - -const PIN_PART_LENGTH = 4; - -Cu.import("resource://services-sync/main.js"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/PlacesUtils.jsm"); -Cu.import("resource://gre/modules/PluralForm.jsm"); - - -function setVisibility(element, visible) { - element.style.visibility = visible ? "visible" : "hidden"; -} - -var gSyncSetup = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, - Ci.nsIWebProgressListener, - Ci.nsISupportsWeakReference]), - - captchaBrowser: null, - wizard: null, - _disabledSites: [], - - status: { - password: false, - email: false, - server: false - }, - - get _remoteSites() [Weave.Service.serverURL, RECAPTCHA_DOMAIN], - - get _usingMainServers() { - if (this._settingUpNew) - return document.getElementById("server").selectedIndex == 0; - return document.getElementById("existingServer").selectedIndex == 0; - }, - - init: function () { - let obs = [ - ["weave:service:change-passphrase", "onResetPassphrase"], - ["weave:service:login:start", "onLoginStart"], - ["weave:service:login:error", "onLoginEnd"], - ["weave:service:login:finish", "onLoginEnd"]]; - - // Add the observers now and remove them on unload - let self = this; - let addRem = function(add) { - obs.forEach(function([topic, func]) { - //XXXzpao This should use Services.obs.* but Weave's Obs does nice handling - // of `this`. Fix in a followup. (bug 583347) - if (add) - Weave.Svc.Obs.add(topic, self[func], self); - else - Weave.Svc.Obs.remove(topic, self[func], self); - }); - }; - addRem(true); - window.addEventListener("unload", function() addRem(false), false); - - window.setTimeout(function () { - // Force Service to be loaded so that engines are registered. - // See Bug 670082. - Weave.Service; - }, 0); - - this.captchaBrowser = document.getElementById("captcha"); - - this.wizardType = null; - if (window.arguments && window.arguments[0]) { - this.wizardType = window.arguments[0]; - } - switch (this.wizardType) { - case null: - this.wizard.pageIndex = INTRO_PAGE; - // Fall through! - case "pair": - this.captchaBrowser.addProgressListener(this); - Weave.Svc.Prefs.set("firstSync", "notReady"); - break; - case "reset": - this._resettingSync = true; - this.wizard.pageIndex = OPTIONS_PAGE; - break; - } - - this.wizard.getButton("extra1").label = - this._stringBundle.GetStringFromName("button.syncOptions.label"); - - // Remember these values because the options pages change them temporarily. - this._nextButtonLabel = this.wizard.getButton("next").label; - this._nextButtonAccesskey = this.wizard.getButton("next") - .getAttribute("accesskey"); - this._backButtonLabel = this.wizard.getButton("back").label; - this._backButtonAccesskey = this.wizard.getButton("back") - .getAttribute("accesskey"); - }, - - startNewAccountSetup: function () { - if (!Weave.Utils.ensureMPUnlocked()) - return false; - this._settingUpNew = true; - this.wizard.pageIndex = NEW_ACCOUNT_START_PAGE; - }, - - useExistingAccount: function () { - if (!Weave.Utils.ensureMPUnlocked()) - return false; - this._settingUpNew = false; - if (this.wizardType == "pair") { - // We're already pairing, so there's no point in pairing again. - // Go straight to the manual login page. - this.wizard.pageIndex = EXISTING_ACCOUNT_LOGIN_PAGE; - } else { - this.wizard.pageIndex = EXISTING_ACCOUNT_CONNECT_PAGE; - } - }, - - resetPassphrase: function resetPassphrase() { - // Apply the existing form fields so that - // Weave.Service.changePassphrase() has the necessary credentials. - Weave.Service.identity.account = document.getElementById("existingAccountName").value; - Weave.Service.identity.basicPassword = document.getElementById("existingPassword").value; - - // Generate a new passphrase so that Weave.Service.login() will - // actually do something. - let passphrase = Weave.Utils.generatePassphrase(); - Weave.Service.identity.syncKey = passphrase; - - // Only open the dialog if username + password are actually correct. - Weave.Service.login(); - if ([Weave.LOGIN_FAILED_INVALID_PASSPHRASE, - Weave.LOGIN_FAILED_NO_PASSPHRASE, - Weave.LOGIN_SUCCEEDED].indexOf(Weave.Status.login) == -1) { - return; - } - - // Hide any errors about the passphrase, we know it's not right. - let feedback = document.getElementById("existingPassphraseFeedbackRow"); - feedback.hidden = true; - let el = document.getElementById("existingPassphrase"); - el.value = Weave.Utils.hyphenatePassphrase(passphrase); - - // changePassphrase() will sync, make sure we set the "firstSync" pref - // according to the user's pref. - Weave.Svc.Prefs.reset("firstSync"); - this.setupInitialSync(); - gSyncUtils.resetPassphrase(true); - }, - - onResetPassphrase: function () { - document.getElementById("existingPassphrase").value = - Weave.Utils.hyphenatePassphrase(Weave.Service.identity.syncKey); - this.checkFields(); - this.wizard.advance(); - }, - - onLoginStart: function () { - this.toggleLoginFeedback(false); - }, - - onLoginEnd: function () { - this.toggleLoginFeedback(true); - }, - - sendCredentialsAfterSync: function () { - let send = function() { - Services.obs.removeObserver("weave:service:sync:finish", send); - Services.obs.removeObserver("weave:service:sync:error", send); - let credentials = {account: Weave.Service.identity.account, - password: Weave.Service.identity.basicPassword, - synckey: Weave.Service.identity.syncKey, - serverURL: Weave.Service.serverURL}; - this._jpakeclient.sendAndComplete(credentials); - }.bind(this); - Services.obs.addObserver("weave:service:sync:finish", send, false); - Services.obs.addObserver("weave:service:sync:error", send, false); - }, - - toggleLoginFeedback: function (stop) { - document.getElementById("login-throbber").hidden = stop; - let password = document.getElementById("existingPasswordFeedbackRow"); - let server = document.getElementById("existingServerFeedbackRow"); - let passphrase = document.getElementById("existingPassphraseFeedbackRow"); - - if (!stop || (Weave.Status.login == Weave.LOGIN_SUCCEEDED)) { - password.hidden = server.hidden = passphrase.hidden = true; - return; - } - - let feedback; - switch (Weave.Status.login) { - case Weave.LOGIN_FAILED_NETWORK_ERROR: - case Weave.LOGIN_FAILED_SERVER_ERROR: - feedback = server; - break; - case Weave.LOGIN_FAILED_LOGIN_REJECTED: - case Weave.LOGIN_FAILED_NO_USERNAME: - case Weave.LOGIN_FAILED_NO_PASSWORD: - feedback = password; - break; - case Weave.LOGIN_FAILED_INVALID_PASSPHRASE: - feedback = passphrase; - break; - } - this._setFeedbackMessage(feedback, false, Weave.Status.login); - }, - - setupInitialSync: function () { - let action = document.getElementById("mergeChoiceRadio").selectedItem.id; - switch (action) { - case "resetClient": - // if we're not resetting sync, we don't need to explicitly - // call resetClient - if (!this._resettingSync) - return; - // otherwise, fall through - case "wipeClient": - case "wipeRemote": - Weave.Svc.Prefs.set("firstSync", action); - break; - } - }, - - // fun with validation! - checkFields: function () { - this.wizard.canAdvance = this.readyToAdvance(); - }, - - readyToAdvance: function () { - switch (this.wizard.pageIndex) { - case INTRO_PAGE: - return false; - case NEW_ACCOUNT_START_PAGE: - for (let i in this.status) { - if (!this.status[i]) - return false; - } - if (this._usingMainServers) - return document.getElementById("tos").checked; - - return true; - case EXISTING_ACCOUNT_LOGIN_PAGE: - let hasUser = document.getElementById("existingAccountName").value != ""; - let hasPass = document.getElementById("existingPassword").value != ""; - let hasKey = document.getElementById("existingPassphrase").value != ""; - - if (hasUser && hasPass && hasKey) { - if (this._usingMainServers) - return true; - - if (this._validateServer(document.getElementById("existingServer"))) { - return true; - } - } - return false; - } - // Default, e.g. wizard's special page -1 etc. - return true; - }, - - onPINInput: function onPINInput(textbox) { - if (textbox && textbox.value.length == PIN_PART_LENGTH) { - this.nextFocusEl[textbox.id].focus(); - } - this.wizard.canAdvance = (this.pin1.value.length == PIN_PART_LENGTH && - this.pin2.value.length == PIN_PART_LENGTH && - this.pin3.value.length == PIN_PART_LENGTH); - }, - - onEmailInput: function () { - // Check account validity when the user stops typing for 1 second. - if (this._checkAccountTimer) - window.clearTimeout(this._checkAccountTimer); - this._checkAccountTimer = window.setTimeout(function () { - gSyncSetup.checkAccount(); - }, 1000); - }, - - checkAccount: function() { - delete this._checkAccountTimer; - let value = Weave.Utils.normalizeAccount( - document.getElementById("weaveEmail").value); - if (!value) { - this.status.email = false; - this.checkFields(); - return; - } - - let re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; - let feedback = document.getElementById("emailFeedbackRow"); - let valid = re.test(value); - - let str = ""; - if (!valid) { - str = "invalidEmail.label"; - } else { - let availCheck = Weave.Service.checkAccount(value); - valid = availCheck == "available"; - if (!valid) { - if (availCheck == "notAvailable") - str = "usernameNotAvailable.label"; - else - str = availCheck; - } - } - - this._setFeedbackMessage(feedback, valid, str); - this.status.email = valid; - if (valid) - Weave.Service.identity.account = value; - this.checkFields(); - }, - - onPasswordChange: function () { - let password = document.getElementById("weavePassword"); - let pwconfirm = document.getElementById("weavePasswordConfirm"); - let [valid, errorString] = gSyncUtils.validatePassword(password, pwconfirm); - - let feedback = document.getElementById("passwordFeedbackRow"); - this._setFeedback(feedback, valid, errorString); - - this.status.password = valid; - this.checkFields(); - }, - - onPageShow: function() { - switch (this.wizard.pageIndex) { - case PAIR_PAGE: - this.wizard.getButton("back").hidden = true; - this.wizard.getButton("extra1").hidden = true; - this.onPINInput(); - this.pin1.focus(); - break; - case INTRO_PAGE: - // We may not need the captcha in the Existing Account branch of the - // wizard. However, we want to preload it to avoid any flickering while - // the Create Account page is shown. - this.loadCaptcha(); - this.wizard.getButton("next").hidden = true; - this.wizard.getButton("back").hidden = true; - this.wizard.getButton("extra1").hidden = true; - this.checkFields(); - break; - case NEW_ACCOUNT_START_PAGE: - this.wizard.getButton("extra1").hidden = false; - this.wizard.getButton("next").hidden = false; - this.wizard.getButton("back").hidden = false; - this.onServerCommand(); - this.wizard.canRewind = true; - this.checkFields(); - break; - case EXISTING_ACCOUNT_CONNECT_PAGE: - Weave.Svc.Prefs.set("firstSync", "existingAccount"); - this.wizard.getButton("next").hidden = false; - this.wizard.getButton("back").hidden = false; - this.wizard.getButton("extra1").hidden = false; - this.wizard.canAdvance = false; - this.wizard.canRewind = true; - this.startEasySetup(); - break; - case EXISTING_ACCOUNT_LOGIN_PAGE: - this.wizard.getButton("next").hidden = false; - this.wizard.getButton("back").hidden = false; - this.wizard.getButton("extra1").hidden = false; - this.wizard.canRewind = true; - this.checkFields(); - break; - case OPTIONS_PAGE: - this.wizard.canRewind = false; - this.wizard.canAdvance = true; - if (!this._resettingSync) { - this.wizard.getButton("next").label = - this._stringBundle.GetStringFromName("button.syncOptionsDone.label"); - this.wizard.getButton("next").removeAttribute("accesskey"); - } - this.wizard.getButton("next").hidden = false; - this.wizard.getButton("back").hidden = true; - this.wizard.getButton("cancel").hidden = !this._resettingSync; - this.wizard.getButton("extra1").hidden = true; - document.getElementById("syncComputerName").value = Weave.Service.clientsEngine.localName; - document.getElementById("syncOptions").collapsed = this._resettingSync; - document.getElementById("mergeOptions").collapsed = this._settingUpNew; - break; - case OPTIONS_CONFIRM_PAGE: - this.wizard.canRewind = true; - this.wizard.canAdvance = true; - this.wizard.getButton("back").label = - this._stringBundle.GetStringFromName("button.syncOptionsCancel.label"); - this.wizard.getButton("back").removeAttribute("accesskey"); - this.wizard.getButton("back").hidden = this._resettingSync; - this.wizard.getButton("next").hidden = false; - this.wizard.getButton("finish").hidden = true; - break; - } - }, - - onWizardAdvance: function () { - // Check pageIndex so we don't prompt before the Sync setup wizard appears. - // This is a fallback in case the Master Password gets locked mid-wizard. - if ((this.wizard.pageIndex >= 0) && - !Weave.Utils.ensureMPUnlocked()) { - return false; - } - - switch (this.wizard.pageIndex) { - case PAIR_PAGE: - this.startPairing(); - return false; - case NEW_ACCOUNT_START_PAGE: - // If the user selects Next (e.g. by hitting enter) when we haven't - // executed the delayed checks yet, execute them immediately. - if (this._checkAccountTimer) { - this.checkAccount(); - } - if (this._checkServerTimer) { - this.checkServer(); - } - if (!this.wizard.canAdvance) { - return false; - } - - let doc = this.captchaBrowser.contentDocument; - let getField = function getField(field) { - let node = doc.getElementById("recaptcha_" + field + "_field"); - return node && node.value; - }; - - // Display throbber - let feedback = document.getElementById("captchaFeedback"); - let image = feedback.firstChild; - let label = image.nextSibling; - image.setAttribute("status", "active"); - label.value = this._stringBundle.GetStringFromName("verifying.label"); - setVisibility(feedback, true); - - let password = document.getElementById("weavePassword").value; - let email = Weave.Utils.normalizeAccount( - document.getElementById("weaveEmail").value); - let challenge = getField("challenge"); - let response = getField("response"); - - let error = Weave.Service.createAccount(email, password, - challenge, response); - - if (error == null) { - Weave.Service.identity.account = email; - Weave.Service.identity.basicPassword = password; - Weave.Service.identity.syncKey = Weave.Utils.generatePassphrase(); - this._handleNoScript(false); - Weave.Svc.Prefs.set("firstSync", "newAccount"); - this.wizardFinish(); - return false; - } - - image.setAttribute("status", "error"); - label.value = Weave.Utils.getErrorString(error); - return false; - case EXISTING_ACCOUNT_LOGIN_PAGE: - Weave.Service.identity.account = Weave.Utils.normalizeAccount( - document.getElementById("existingAccountName").value); - Weave.Service.identity.basicPassword = - document.getElementById("existingPassword").value; - let pp = document.getElementById("existingPassphrase").value; - Weave.Service.identity.syncKey = Weave.Utils.normalizePassphrase(pp); - if (Weave.Service.login()) { - this.wizardFinish(); - } - return false; - case OPTIONS_PAGE: - let desc = document.getElementById("mergeChoiceRadio").selectedIndex; - // No confirmation needed on new account setup or merge option - // with existing account. - if (this._settingUpNew || (!this._resettingSync && desc == 0)) - return this.returnFromOptions(); - return this._handleChoice(); - case OPTIONS_CONFIRM_PAGE: - if (this._resettingSync) { - this.wizardFinish(); - return false; - } - return this.returnFromOptions(); - } - return true; - }, - - onWizardBack: function () { - switch (this.wizard.pageIndex) { - case NEW_ACCOUNT_START_PAGE: - case EXISTING_ACCOUNT_LOGIN_PAGE: - this.wizard.pageIndex = INTRO_PAGE; - return false; - case EXISTING_ACCOUNT_CONNECT_PAGE: - this.abortEasySetup(); - this.wizard.pageIndex = INTRO_PAGE; - return false; - case EXISTING_ACCOUNT_LOGIN_PAGE: - // If we were already pairing on entry, we went straight to the manual - // login page. If subsequently we go back, return to the page that lets - // us choose whether we already have an account. - if (this.wizardType == "pair") { - this.wizard.pageIndex = INTRO_PAGE; - return false; - } - return true; - case OPTIONS_CONFIRM_PAGE: - // Backing up from the confirmation page = resetting first sync to merge. - document.getElementById("mergeChoiceRadio").selectedIndex = 0; - return this.returnFromOptions(); - } - return true; - }, - - wizardFinish: function () { - this.setupInitialSync(); - - if (this.wizardType == "pair") { - this.completePairing(); - } - - if (!this._resettingSync) { - function isChecked(element) { - return document.getElementById(element).hasAttribute("checked"); - } - - let prefs = ["engine.bookmarks", "engine.passwords", "engine.history", - "engine.tabs", "engine.prefs", "engine.addons"]; - for (let i = 0;i < prefs.length;i++) { - Weave.Svc.Prefs.set(prefs[i], isChecked(prefs[i])); - } - - // XXX: Addons syncing is currently not operational; - // Make doubly-sure to always disable addons syncing pref - Weave.Svc.Prefs.set("engine.addons", false); - - this._handleNoScript(false); - if (Weave.Svc.Prefs.get("firstSync", "") == "notReady") - Weave.Svc.Prefs.reset("firstSync"); - - Weave.Service.persistLogin(); - Weave.Svc.Obs.notify("weave:service:setup-complete"); - - gSyncUtils.openFirstSyncProgressPage(); - } - Weave.Utils.nextTick(Weave.Service.sync, Weave.Service); - window.close(); - }, - - onWizardCancel: function () { - if (this._resettingSync) - return; - - this.abortEasySetup(); - this._handleNoScript(false); - Weave.Service.startOver(); - }, - - onSyncOptions: function () { - this._beforeOptionsPage = this.wizard.pageIndex; - this.wizard.pageIndex = OPTIONS_PAGE; - }, - - returnFromOptions: function() { - this.wizard.getButton("next").label = this._nextButtonLabel; - this.wizard.getButton("next").setAttribute("accesskey", - this._nextButtonAccesskey); - this.wizard.getButton("back").label = this._backButtonLabel; - this.wizard.getButton("back").setAttribute("accesskey", - this._backButtonAccesskey); - this.wizard.getButton("cancel").hidden = false; - this.wizard.getButton("extra1").hidden = false; - this.wizard.pageIndex = this._beforeOptionsPage; - return false; - }, - - startPairing: function startPairing() { - this.pairDeviceErrorRow.hidden = true; - // When onAbort is called, Weave may already be gone. - const JPAKE_ERROR_USERABORT = Weave.JPAKE_ERROR_USERABORT; - - let self = this; - let jpakeclient = this._jpakeclient = new Weave.JPAKEClient({ - onPaired: function onPaired() { - self.wizard.pageIndex = INTRO_PAGE; - }, - onComplete: function onComplete() { - // This method will never be called since SendCredentialsController - // will take over after the wizard completes. - }, - onAbort: function onAbort(error) { - delete self._jpakeclient; - - // Aborted by user, ignore. The window is almost certainly going to close - // or is already closed. - if (error == JPAKE_ERROR_USERABORT) { - return; - } - - self.pairDeviceErrorRow.hidden = false; - self.pairDeviceThrobber.hidden = true; - self.pin1.value = self.pin2.value = self.pin3.value = ""; - self.pin1.disabled = self.pin2.disabled = self.pin3.disabled = false; - if (self.wizard.pageIndex == PAIR_PAGE) { - self.pin1.focus(); - } - } - }); - this.pairDeviceThrobber.hidden = false; - this.pin1.disabled = this.pin2.disabled = this.pin3.disabled = true; - this.wizard.canAdvance = false; - - let pin = this.pin1.value + this.pin2.value + this.pin3.value; - let expectDelay = true; - jpakeclient.pairWithPIN(pin, expectDelay); - }, - - completePairing: function completePairing() { - if (!this._jpakeclient) { - // The channel was aborted while we were setting up the account - // locally. XXX TODO should we do anything here, e.g. tell - // the user on the last wizard page that it's ok, they just - // have to pair again? - return; - } - let controller = new Weave.SendCredentialsController(this._jpakeclient, - Weave.Service); - this._jpakeclient.controller = controller; - }, - - startEasySetup: function () { - // Don't do anything if we have a client already (e.g. we went to - // Sync Options and just came back). - if (this._jpakeclient) - return; - - // When onAbort is called, Weave may already be gone - const JPAKE_ERROR_USERABORT = Weave.JPAKE_ERROR_USERABORT; - - let self = this; - this._jpakeclient = new Weave.JPAKEClient({ - displayPIN: function displayPIN(pin) { - document.getElementById("easySetupPIN1").value = pin.slice(0, 4); - document.getElementById("easySetupPIN2").value = pin.slice(4, 8); - document.getElementById("easySetupPIN3").value = pin.slice(8); - }, - - onPairingStart: function onPairingStart() {}, - - onComplete: function onComplete(credentials) { - Weave.Service.identity.account = credentials.account; - Weave.Service.identity.basicPassword = credentials.password; - Weave.Service.identity.syncKey = credentials.synckey; - Weave.Service.serverURL = credentials.serverURL; - gSyncSetup.wizardFinish(); - }, - - onAbort: function onAbort(error) { - delete self._jpakeclient; - - // Ignore if wizard is aborted. - if (error == JPAKE_ERROR_USERABORT) - return; - - // Automatically go to manual setup if we couldn't acquire a channel. - if (error == Weave.JPAKE_ERROR_CHANNEL) { - self.wizard.pageIndex = EXISTING_ACCOUNT_LOGIN_PAGE; - return; - } - - // Restart on all other errors. - self.startEasySetup(); - } - }); - this._jpakeclient.receiveNoPIN(); - }, - - abortEasySetup: function () { - document.getElementById("easySetupPIN1").value = ""; - document.getElementById("easySetupPIN2").value = ""; - document.getElementById("easySetupPIN3").value = ""; - if (!this._jpakeclient) - return; - - this._jpakeclient.abort(); - delete this._jpakeclient; - }, - - manualSetup: function () { - this.abortEasySetup(); - this.wizard.pageIndex = EXISTING_ACCOUNT_LOGIN_PAGE; - }, - - // _handleNoScript is needed because it blocks the captcha. So we temporarily - // allow the necessary sites so that we can verify the user is in fact a human. - // This was done with the help of Giorgio (NoScript author). See bug 508112. - _handleNoScript: function (addExceptions) { - // if NoScript isn't installed, or is disabled, bail out. - let ns = Cc["@maone.net/noscript-service;1"]; - if (ns == null) - return; - - ns = ns.getService().wrappedJSObject; - if (addExceptions) { - this._remoteSites.forEach(function(site) { - site = ns.getSite(site); - if (!ns.isJSEnabled(site)) { - this._disabledSites.push(site); // save status - ns.setJSEnabled(site, true); // allow site - } - }, this); - } - else { - this._disabledSites.forEach(function(site) { - ns.setJSEnabled(site, false); - }); - this._disabledSites = []; - } - }, - - onExistingServerCommand: function () { - let control = document.getElementById("existingServer"); - if (control.selectedIndex == 0) { - control.removeAttribute("editable"); - Weave.Svc.Prefs.reset("serverURL"); - } else { - control.setAttribute("editable", "true"); - // Force a style flush to ensure that the binding is attached. - control.clientTop; - control.value = ""; - control.inputField.focus(); - } - document.getElementById("existingServerFeedbackRow").hidden = true; - this.checkFields(); - }, - - onExistingServerInput: function () { - // Check custom server validity when the user stops typing for 1 second. - if (this._existingServerTimer) - window.clearTimeout(this._existingServerTimer); - this._existingServerTimer = window.setTimeout(function () { - gSyncSetup.checkFields(); - }, 1000); - }, - - onServerCommand: function () { - setVisibility(document.getElementById("TOSRow"), this._usingMainServers); - let control = document.getElementById("server"); - if (!this._usingMainServers) { - control.setAttribute("editable", "true"); - // Force a style flush to ensure that the binding is attached. - control.clientTop; - control.value = ""; - control.inputField.focus(); - // checkServer() will call checkAccount() and checkFields(). - this.checkServer(); - return; - } - control.removeAttribute("editable"); - Weave.Svc.Prefs.reset("serverURL"); - if (this._settingUpNew) { - this.loadCaptcha(); - } - this.checkAccount(); - this.status.server = true; - document.getElementById("serverFeedbackRow").hidden = true; - this.checkFields(); - }, - - onServerInput: function () { - // Check custom server validity when the user stops typing for 1 second. - if (this._checkServerTimer) - window.clearTimeout(this._checkServerTimer); - this._checkServerTimer = window.setTimeout(function () { - gSyncSetup.checkServer(); - }, 1000); - }, - - checkServer: function () { - delete this._checkServerTimer; - let el = document.getElementById("server"); - let valid = false; - let feedback = document.getElementById("serverFeedbackRow"); - let str = ""; - if (el.value) { - valid = this._validateServer(el); - let str = valid ? "" : "serverInvalid.label"; - this._setFeedbackMessage(feedback, valid, str); - } - else - this._setFeedbackMessage(feedback, true); - - // Recheck account against the new server. - if (valid) - this.checkAccount(); - - this.status.server = valid; - this.checkFields(); - }, - - _validateServer: function (element) { - let valid = false; - let val = element.value; - if (!val) - return false; - - let uri = Weave.Utils.makeURI(val); - - if (!uri) - uri = Weave.Utils.makeURI("https://" + val); - - if (uri && this._settingUpNew) { - function isValid(uri) { - Weave.Service.serverURL = uri.spec; - let check = Weave.Service.checkAccount("a"); - return (check == "available" || check == "notAvailable"); - } - - if (uri.schemeIs("http")) { - uri.scheme = "https"; - if (isValid(uri)) - valid = true; - else - // setting the scheme back to http - uri.scheme = "http"; - } - if (!valid) - valid = isValid(uri); - - if (valid) { - this.loadCaptcha(); - } - } - else if (uri) { - valid = true; - Weave.Service.serverURL = uri.spec; - } - - if (valid) - element.value = Weave.Service.serverURL; - else - Weave.Svc.Prefs.reset("serverURL"); - - return valid; - }, - - _handleChoice: function () { - let desc = document.getElementById("mergeChoiceRadio").selectedIndex; - document.getElementById("chosenActionDeck").selectedIndex = desc; - switch (desc) { - case 1: - if (this._case1Setup) - break; - - let places_db = PlacesUtils.history - .QueryInterface(Ci.nsPIPlacesDatabase) - .DBConnection; - if (Weave.Service.engineManager.get("history").enabled) { - let daysOfHistory = 0; - let stm = places_db.createStatement( - "SELECT ROUND(( " + - "strftime('%s','now','localtime','utc') - " + - "( " + - "SELECT visit_date FROM moz_historyvisits " + - "ORDER BY visit_date ASC LIMIT 1 " + - ")/1000000 " + - ")/86400) AS daysOfHistory "); - - if (stm.step()) - daysOfHistory = stm.getInt32(0); - // Support %S for historical reasons (see bug 600141) - document.getElementById("historyCount").value = - PluralForm.get(daysOfHistory, - this._stringBundle.GetStringFromName("historyDaysCount.label")) - .replace("%S", daysOfHistory) - .replace("#1", daysOfHistory); - } else { - document.getElementById("historyCount").hidden = true; - } - - if (Weave.Service.engineManager.get("bookmarks").enabled) { - let bookmarks = 0; - let stm = places_db.createStatement( - "SELECT count(*) AS bookmarks " + - "FROM moz_bookmarks b " + - "LEFT JOIN moz_bookmarks t ON " + - "b.parent = t.id WHERE b.type = 1 AND t.parent <> :tag"); - stm.params.tag = PlacesUtils.tagsFolderId; - if (stm.executeStep()) - bookmarks = stm.row.bookmarks; - // Support %S for historical reasons (see bug 600141) - document.getElementById("bookmarkCount").value = - PluralForm.get(bookmarks, - this._stringBundle.GetStringFromName("bookmarksCount.label")) - .replace("%S", bookmarks) - .replace("#1", bookmarks); - } else { - document.getElementById("bookmarkCount").hidden = true; - } - - if (Weave.Service.engineManager.get("passwords").enabled) { - let logins = Services.logins.getAllLogins({}); - // Support %S for historical reasons (see bug 600141) - document.getElementById("passwordCount").value = - PluralForm.get(logins.length, - this._stringBundle.GetStringFromName("passwordsCount.label")) - .replace("%S", logins.length) - .replace("#1", logins.length); - } else { - document.getElementById("passwordCount").hidden = true; - } - - if (!Weave.Service.engineManager.get("prefs").enabled) { - document.getElementById("prefsWipe").hidden = true; - } - - let addonsEngine = Weave.Service.engineManager.get("addons"); - if (addonsEngine.enabled) { - let ids = addonsEngine._store.getAllIDs(); - let blessedcount = 0; - for each (let i in ids) { - if (i) { - blessedcount++; - } - } - // bug 600141 does not apply, as this does not have to support existing strings - document.getElementById("addonCount").value = - PluralForm.get(blessedcount, - this._stringBundle.GetStringFromName("addonsCount.label")) - .replace("#1", blessedcount); - } else { - document.getElementById("addonCount").hidden = true; - } - - this._case1Setup = true; - break; - case 2: - if (this._case2Setup) - break; - let count = 0; - function appendNode(label) { - let box = document.getElementById("clientList"); - let node = document.createElement("label"); - node.setAttribute("value", label); - node.setAttribute("class", "data indent"); - box.appendChild(node); - } - - for each (let name in Weave.Service.clientsEngine.stats.names) { - // Don't list the current client - if (name == Weave.Service.clientsEngine.localName) - continue; - - // Only show the first several client names - if (++count <= 5) - appendNode(name); - } - if (count > 5) { - // Support %S for historical reasons (see bug 600141) - let label = - PluralForm.get(count - 5, - this._stringBundle.GetStringFromName("additionalClientCount.label")) - .replace("%S", count - 5) - .replace("#1", count - 5); - appendNode(label); - } - this._case2Setup = true; - break; - } - - return true; - }, - - // sets class and string on a feedback element - // if no property string is passed in, we clear label/style - _setFeedback: function (element, success, string) { - element.hidden = success || !string; - let classname = success ? "success" : "error"; - let image = element.getElementsByAttribute("class", "statusIcon")[0]; - image.setAttribute("status", classname); - let label = element.getElementsByAttribute("class", "status")[0]; - label.value = string; - }, - - // shim - _setFeedbackMessage: function (element, success, string) { - let str = ""; - if (string) { - try { - str = this._stringBundle.GetStringFromName(string); - } catch(e) {} - - if (!str) - str = Weave.Utils.getErrorString(string); - } - this._setFeedback(element, success, str); - }, - - loadCaptcha: function loadCaptcha() { - let captchaURI = Weave.Service.miscAPI + "captcha_html"; - // First check for NoScript and whitelist the right sites. - this._handleNoScript(true); - if (this.captchaBrowser.currentURI.spec != captchaURI) { - this.captchaBrowser.loadURI(captchaURI); - } - }, - - onStateChange: function(webProgress, request, stateFlags, status) { - // We're only looking for the end of the frame load - if ((stateFlags & Ci.nsIWebProgressListener.STATE_STOP) == 0) - return; - if ((stateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) == 0) - return; - if ((stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) == 0) - return; - - // If we didn't find a captcha, assume it's not needed and don't show it. - let responseStatus = request.QueryInterface(Ci.nsIHttpChannel).responseStatus; - setVisibility(this.captchaBrowser, responseStatus != 404); - //XXX TODO we should really log any responseStatus other than 200 - }, - onProgressChange: function() {}, - onStatusChange: function() {}, - onSecurityChange: function() {}, - onLocationChange: function () {} -}; - -// Define lazy getters for various XUL elements. -// -// onWizardAdvance() and onPageShow() are run before init(), so we'll even -// define things that will almost certainly be used (like 'wizard') as a lazy -// getter here. -["wizard", - "pin1", - "pin2", - "pin3", - "pairDeviceErrorRow", - "pairDeviceThrobber"].forEach(function (id) { - XPCOMUtils.defineLazyGetter(gSyncSetup, id, function() { - return document.getElementById(id); - }); -}); -XPCOMUtils.defineLazyGetter(gSyncSetup, "nextFocusEl", function () { - return {pin1: this.pin2, - pin2: this.pin3, - pin3: this.wizard.getButton("next")}; -}); -XPCOMUtils.defineLazyGetter(gSyncSetup, "_stringBundle", function() { - return Services.strings.createBundle("chrome://browser/locale/syncSetup.properties"); -}); diff --git a/components/sync/setup.xul b/components/sync/setup.xul deleted file mode 100644 index cf2cc77..0000000 --- a/components/sync/setup.xul +++ /dev/null @@ -1,491 +0,0 @@ -<?xml version="1.0"?> - -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> -<?xml-stylesheet href="chrome://browser/skin/syncSetup.css" type="text/css"?> -<?xml-stylesheet href="chrome://browser/skin/syncCommon.css" type="text/css"?> - -<!DOCTYPE window [ -<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> -<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd"> -<!ENTITY % syncSetupDTD SYSTEM "chrome://browser/locale/syncSetup.dtd"> -%brandDTD; -%syncBrandDTD; -%syncSetupDTD; -]> -<wizard id="wizard" - title="&accountSetupTitle.label;" - windowtype="Weave:AccountSetup" - persist="screenX screenY" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:html="http://www.w3.org/1999/xhtml" - onwizardnext="return gSyncSetup.onWizardAdvance()" - onwizardback="return gSyncSetup.onWizardBack()" - onwizardcancel="gSyncSetup.onWizardCancel()" - onload="gSyncSetup.init()"> - - <script type="application/javascript" - src="chrome://browser/content/sync/setup.js"/> - <script type="application/javascript" - src="chrome://browser/content/sync/utils.js"/> - <script type="application/javascript" - src="chrome://browser/content/utilityOverlay.js"/> - <script type="application/javascript" - src="chrome://global/content/printUtils.js"/> - - <wizardpage id="addDevicePage" - label="&pairDevice.title.label;" - onpageshow="gSyncSetup.onPageShow()"> - <description> - &pairDevice.dialog.description.label; - <label class="text-link" - value="&addDevice.showMeHow.label;" - href="http://www.palemoon.org/sync/help/easy-setup.shtml"/> - </description> - <separator class="groove-thin"/> - <description> - &addDevice.dialog.enterCode.label; - </description> - <separator class="groove-thin"/> - <vbox align="center"> - <textbox id="pin1" - class="pin" - oninput="gSyncSetup.onPINInput(this);" - onfocus="this.select();" - /> - <textbox id="pin2" - class="pin" - oninput="gSyncSetup.onPINInput(this);" - onfocus="this.select();" - /> - <textbox id="pin3" - class="pin" - oninput="gSyncSetup.onPINInput(this);" - onfocus="this.select();" - /> - </vbox> - <separator class="groove-thin"/> - <vbox id="pairDeviceThrobber" align="center" hidden="true"> - <image/> - </vbox> - <hbox id="pairDeviceErrorRow" pack="center" hidden="true"> - <image class="statusIcon" status="error"/> - <label class="status" - value="&addDevice.dialog.tryAgain.label;"/> - </hbox> - </wizardpage> - - <wizardpage id="pickSetupType" - label="&syncBrand.fullName.label;" - onpageshow="gSyncSetup.onPageShow()"> - <vbox align="center" flex="1"> - <description style="padding: 0 7em;"> - &setup.pickSetupType.description2; - </description> - <spacer flex="3"/> - <button id="newAccount" - class="accountChoiceButton" - label="&button.createNewAccount.label;" - oncommand="gSyncSetup.startNewAccountSetup()" - align="center"/> - <spacer flex="1"/> - </vbox> - <separator class="groove"/> - <vbox align="center" flex="1"> - <spacer flex="1"/> - <button id="existingAccount" - class="accountChoiceButton" - label="&button.haveAccount.label;" - oncommand="gSyncSetup.useExistingAccount()"/> - <spacer flex="3"/> - </vbox> - </wizardpage> - - <wizardpage label="&setup.newAccountDetailsPage.title.label;" - id="newAccountStart" - onextra1="gSyncSetup.onSyncOptions()" - onpageshow="gSyncSetup.onPageShow();"> - <grid> - <columns> - <column/> - <column class="inputColumn" flex="1"/> - </columns> - <rows> - <row id="emailRow" align="center"> - <label value="&setup.emailAddress.label;" - accesskey="&setup.emailAddress.accesskey;" - control="weaveEmail"/> - <textbox id="weaveEmail" - oninput="gSyncSetup.onEmailInput()"/> - </row> - <row id="emailFeedbackRow" align="center" hidden="true"> - <spacer/> - <hbox> - <image class="statusIcon"/> - <label class="status" value=" "/> - </hbox> - </row> - <row id="passwordRow" align="center"> - <label value="&setup.choosePassword.label;" - accesskey="&setup.choosePassword.accesskey;" - control="weavePassword"/> - <textbox id="weavePassword" - type="password" - onchange="gSyncSetup.onPasswordChange()"/> - </row> - <row id="confirmRow" align="center"> - <label value="&setup.confirmPassword.label;" - accesskey="&setup.confirmPassword.accesskey;" - control="weavePasswordConfirm"/> - <textbox id="weavePasswordConfirm" - type="password" - onchange="gSyncSetup.onPasswordChange()"/> - </row> - <row id="passwordFeedbackRow" align="center" hidden="true"> - <spacer/> - <hbox> - <image class="statusIcon"/> - <label class="status" value=" "/> - </hbox> - </row> - <row align="center"> - <label control="server" - value="&server.label;"/> - <menulist id="server" - oncommand="gSyncSetup.onServerCommand()" - oninput="gSyncSetup.onServerInput()"> - <menupopup> - <menuitem label="&serverType.default.label;" - value="main"/> - <menuitem label="&serverType.custom2.label;" - value="custom"/> - </menupopup> - </menulist> - </row> - <row id="serverFeedbackRow" align="center" hidden="true"> - <spacer/> - <hbox> - <image class="statusIcon"/> - <label class="status" value=" "/> - </hbox> - </row> - <row id="TOSRow" align="center"> - <spacer/> - <hbox align="center"> - <checkbox id="tos" - accesskey="&setup.tosAgree1.accesskey;" - oncommand="this.focus(); gSyncSetup.checkFields();"/> - <description id="tosDesc" - flex="1" - onclick="document.getElementById('tos').focus(); - document.getElementById('tos').click()"> - &setup.tosAgree1.label; - <label class="text-link inline-link" - onclick="event.stopPropagation();gSyncUtils.openToS();"> - &setup.tosLink.label; - </label> - &setup.tosAgree2.label; - <label class="text-link inline-link" - onclick="event.stopPropagation();gSyncUtils.openPrivacyPolicy();"> - &setup.ppLink.label; - </label> - &setup.tosAgree3.label; - </description> - </hbox> - </row> - </rows> - </grid> - <spacer flex="1"/> - <vbox flex="1" align="center"> - <browser height="150" - width="500" - id="captcha" - type="content" - disablehistory="true"/> - <spacer flex="1"/> - <hbox id="captchaFeedback"> - <image class="statusIcon"/> - <label class="status" value=" "/> - </hbox> - </vbox> - </wizardpage> - - <wizardpage id="addDevice" - label="&pairDevice.title.label;" - onextra1="gSyncSetup.onSyncOptions()" - onpageshow="gSyncSetup.onPageShow()"> - <description> - &pairDevice.setup.description.label; - <label class="text-link" - value="&addDevice.showMeHow.label;" - href="http://www.palemoon.org/sync/help/easy-setup.shtml"/> - </description> - <label value="&addDevice.setup.enterCode.label;" - control="easySetupPIN1"/> - <spacer flex="1"/> - <vbox align="center" flex="1"> - <textbox id="easySetupPIN1" - class="pin" - value="" - readonly="true" - /> - <textbox id="easySetupPIN2" - class="pin" - value="" - readonly="true" - /> - <textbox id="easySetupPIN3" - class="pin" - value="" - readonly="true" - /> - </vbox> - <spacer flex="3"/> - <label class="text-link" - value="&addDevice.dontHaveDevice.label;" - onclick="gSyncSetup.manualSetup();"/> - </wizardpage> - - <wizardpage id="existingAccount" - label="&setup.signInPage.title.label;" - onextra1="gSyncSetup.onSyncOptions()" - onpageshow="gSyncSetup.onPageShow()"> - <grid> - <columns> - <column/> - <column class="inputColumn" flex="1"/> - </columns> - <rows> - <row id="existingAccountRow" align="center"> - <label id="existingAccountLabel" - value="&signIn.account2.label;" - accesskey="&signIn.account2.accesskey;" - control="existingAccount"/> - <textbox id="existingAccountName" - oninput="gSyncSetup.checkFields(event)" - onchange="gSyncSetup.checkFields(event)"/> - </row> - <row id="existingPasswordRow" align="center"> - <label id="existingPasswordLabel" - value="&signIn.password.label;" - accesskey="&signIn.password.accesskey;" - control="existingPassword"/> - <textbox id="existingPassword" - type="password" - onkeyup="gSyncSetup.checkFields(event)" - onchange="gSyncSetup.checkFields(event)"/> - </row> - <row id="existingPasswordFeedbackRow" align="center" hidden="true"> - <spacer/> - <hbox> - <image class="statusIcon"/> - <label class="status" value=" "/> - </hbox> - </row> - <row align="center"> - <spacer/> - <label class="text-link" - value="&resetPassword.label;" - onclick="gSyncUtils.resetPassword(); return false;"/> - </row> - <row align="center"> - <label control="existingServer" - value="&server.label;"/> - <menulist id="existingServer" - oncommand="gSyncSetup.onExistingServerCommand()" - oninput="gSyncSetup.onExistingServerInput()"> - <menupopup> - <menuitem label="&serverType.default.label;" - value="main"/> - <menuitem label="&serverType.custom2.label;" - value="custom"/> - </menupopup> - </menulist> - </row> - <row id="existingServerFeedbackRow" align="center" hidden="true"> - <spacer/> - <hbox> - <image class="statusIcon"/> - <vbox> - <label class="status" value=" "/> - </vbox> - </hbox> - </row> - </rows> - </grid> - - <groupbox> - <label id="existingPassphraseLabel" - value="&signIn.recoveryKey.label;" - accesskey="&signIn.recoveryKey.accesskey;" - control="existingPassphrase"/> - <textbox id="existingPassphrase" - oninput="gSyncSetup.checkFields()"/> - <hbox id="login-throbber" hidden="true"> - <image/> - <label value="&verifying.label;"/> - </hbox> - <vbox align="left" id="existingPassphraseFeedbackRow" hidden="true"> - <hbox> - <image class="statusIcon"/> - <label class="status" value=" "/> - </hbox> - </vbox> - </groupbox> - - <vbox id="passphraseHelpBox"> - <description> - &existingRecoveryKey.description; - <label class="text-link" - href="http://www.palemoon.org/sync/help/recoverykey.shtml"> - &addDevice.showMeHow.label; - </label> - <spacer id="passphraseHelpSpacer"/> - <label class="text-link" - onclick="gSyncSetup.resetPassphrase(); return false;"> - &resetSyncKey.label; - </label> - </description> - </vbox> - </wizardpage> - - <wizardpage id="syncOptionsPage" - label="&setup.optionsPage.title;" - onpageshow="gSyncSetup.onPageShow()"> - <groupbox id="syncOptions"> - <grid> - <columns> - <column/> - <column flex="1" style="-moz-margin-end: 2px"/> - </columns> - <rows> - <row align="center"> - <label value="&syncDeviceName.label;" - accesskey="&syncDeviceName.accesskey;" - control="syncComputerName"/> - <textbox id="syncComputerName" flex="1" - onchange="gSyncUtils.changeName(this)"/> - </row> - <row> - <label value="&syncMy.label;" /> - <vbox> - <checkbox label="&engine.addons.label;" - accesskey="&engine.addons.accesskey;" - id="engine.addons" - checked="false" - hidden="true"/> - <checkbox label="&engine.bookmarks.label;" - accesskey="&engine.bookmarks.accesskey;" - id="engine.bookmarks" - checked="true"/> - <checkbox label="&engine.passwords.label;" - accesskey="&engine.passwords.accesskey;" - id="engine.passwords" - checked="true"/> - <checkbox label="&engine.prefs.label;" - accesskey="&engine.prefs.accesskey;" - id="engine.prefs" - checked="true"/> - <checkbox label="&engine.history.label;" - accesskey="&engine.history.accesskey;" - id="engine.history" - checked="true"/> - <checkbox label="&engine.tabs.label;" - accesskey="&engine.tabs.accesskey;" - id="engine.tabs" - checked="true"/> - </vbox> - </row> - </rows> - </grid> - </groupbox> - - <groupbox id="mergeOptions"> - <radiogroup id="mergeChoiceRadio" pack="start"> - <grid> - <columns> - <column/> - <column flex="1"/> - </columns> - <rows flex="1"> - <row align="center"> - <radio id="resetClient" - class="mergeChoiceButton" - aria-labelledby="resetClientLabel"/> - <label id="resetClientLabel" control="resetClient"> - <html:strong>&choice2.merge.recommended.label;</html:strong> - &choice2a.merge.main.label; - </label> - </row> - <row align="center"> - <radio id="wipeClient" - class="mergeChoiceButton" - aria-labelledby="wipeClientLabel"/> - <label id="wipeClientLabel" - control="wipeClient"> - &choice2a.client.main.label; - </label> - </row> - <row align="center"> - <radio id="wipeRemote" - class="mergeChoiceButton" - aria-labelledby="wipeRemoteLabel"/> - <label id="wipeRemoteLabel" - control="wipeRemote"> - &choice2a.server.main.label; - </label> - </row> - </rows> - </grid> - </radiogroup> - </groupbox> - </wizardpage> - - <wizardpage id="syncOptionsConfirm" - label="&setup.optionsConfirmPage.title;" - onpageshow="gSyncSetup.onPageShow()"> - <deck id="chosenActionDeck"> - <vbox id="chosenActionMerge" class="confirm"> - <description class="normal"> - &confirm.merge2.label; - </description> - </vbox> - <vbox id="chosenActionWipeClient" class="confirm"> - <description class="normal"> - &confirm.client3.label; - </description> - <separator class="thin"/> - <vbox id="dataList"> - <label class="data indent" id="bookmarkCount"/> - <label class="data indent" id="historyCount"/> - <label class="data indent" id="passwordCount"/> - <label class="data indent" id="addonCount"/> - <label class="data indent" id="prefsWipe" - value="&engine.prefs.label;"/> - </vbox> - <separator class="thin"/> - <description class="normal"> - &confirm.client2.moreinfo.label; - </description> - </vbox> - <vbox id="chosenActionWipeServer" class="confirm"> - <description class="normal"> - &confirm.server2.label; - </description> - <separator class="thin"/> - <vbox id="clientList"> - </vbox> - </vbox> - </deck> - </wizardpage> - <!-- In terms of the wizard flow shown to the user, the 'syncOptionsConfirm' - page above is not the last wizard page. To prevent the wizard binding from - assuming that it is, we're inserting this dummy page here. This also means - that the wizard needs to always be closed manually via wizardFinish(). --> - <wizardpage> - </wizardpage> -</wizard> - diff --git a/components/sync/utils.js b/components/sync/utils.js deleted file mode 100644 index d41ecf1..0000000 --- a/components/sync/utils.js +++ /dev/null @@ -1,218 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// Equivalent to 0o600 permissions; used for saved Sync Recovery Key. -// This constant can be replaced when the equivalent values are available to -// chrome JS; see Bug 433295 and Bug 757351. -const PERMISSIONS_RWUSR = 0x180; - -// Weave should always exist before before this file gets included. -var gSyncUtils = { - get bundle() { - delete this.bundle; - return this.bundle = Services.strings.createBundle("chrome://browser/locale/syncSetup.properties"); - }, - - // opens in a new window if we're in a modal prefwindow world, in a new tab otherwise - _openLink: function (url) { - let thisDocEl = document.documentElement, - openerDocEl = window.opener && window.opener.document.documentElement; - if (thisDocEl.id == "accountSetup" && window.opener && - openerDocEl.id == "BrowserPreferences" && !openerDocEl.instantApply) - openUILinkIn(url, "window"); - else if (thisDocEl.id == "BrowserPreferences" && !thisDocEl.instantApply) - openUILinkIn(url, "window"); - else if (document.documentElement.id == "change-dialog") - Services.wm.getMostRecentWindow("navigator:browser") - .openUILinkIn(url, "tab"); - else - openUILinkIn(url, "tab"); - }, - - changeName: function changeName(input) { - // Make sure to update to a modified name, e.g., empty-string -> default - Weave.Service.clientsEngine.localName = input.value; - input.value = Weave.Service.clientsEngine.localName; - }, - - openChange: function openChange(type, duringSetup) { - // Just re-show the dialog if it's already open - let openedDialog = Services.wm.getMostRecentWindow("Sync:" + type); - if (openedDialog != null) { - openedDialog.focus(); - return; - } - - // Open up the change dialog - let changeXUL = "chrome://browser/content/sync/genericChange.xul"; - let changeOpt = "centerscreen,chrome,resizable=no"; - Services.ww.activeWindow.openDialog(changeXUL, "", changeOpt, - type, duringSetup); - }, - - changePassword: function () { - if (Weave.Utils.ensureMPUnlocked()) - this.openChange("ChangePassword"); - }, - - resetPassphrase: function (duringSetup) { - if (Weave.Utils.ensureMPUnlocked()) - this.openChange("ResetPassphrase", duringSetup); - }, - - updatePassphrase: function () { - if (Weave.Utils.ensureMPUnlocked()) - this.openChange("UpdatePassphrase"); - }, - - resetPassword: function () { - this._openLink(Weave.Service.pwResetURL); - }, - - openToS: function () { - this._openLink(Weave.Svc.Prefs.get("termsURL")); - }, - - openPrivacyPolicy: function () { - this._openLink(Weave.Svc.Prefs.get("privacyURL")); - }, - - openFirstSyncProgressPage: function () { - this._openLink("about:sync-progress"); - }, - - /** - * Prepare an invisible iframe with the passphrase backup document. - * Used by both the print and saving methods. - * - * @param elid : ID of the form element containing the passphrase. - * @param callback : Function called once the iframe has loaded. - */ - _preparePPiframe: function(elid, callback) { - let pp = document.getElementById(elid).value; - - // Create an invisible iframe whose contents we can print. - let iframe = document.createElement("iframe"); - iframe.setAttribute("src", "chrome://browser/content/sync/key.xhtml"); - iframe.collapsed = true; - document.documentElement.appendChild(iframe); - iframe.contentWindow.addEventListener("load", function() { - iframe.contentWindow.removeEventListener("load", arguments.callee, false); - - // Insert the Sync Key into the page. - let el = iframe.contentDocument.getElementById("synckey"); - el.firstChild.nodeValue = pp; - - // Insert the TOS and Privacy Policy URLs into the page. - let termsURL = Weave.Svc.Prefs.get("termsURL"); - el = iframe.contentDocument.getElementById("tosLink"); - el.setAttribute("href", termsURL); - el.firstChild.nodeValue = termsURL; - - let privacyURL = Weave.Svc.Prefs.get("privacyURL"); - el = iframe.contentDocument.getElementById("ppLink"); - el.setAttribute("href", privacyURL); - el.firstChild.nodeValue = privacyURL; - - callback(iframe); - }, false); - }, - - /** - * Print passphrase backup document. - * - * @param elid : ID of the form element containing the passphrase. - */ - passphrasePrint: function(elid) { - this._preparePPiframe(elid, function(iframe) { - let webBrowserPrint = iframe.contentWindow - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebBrowserPrint); - let printSettings = PrintUtils.getPrintSettings(); - - // Display no header/footer decoration except for the date. - printSettings.headerStrLeft - = printSettings.headerStrCenter - = printSettings.headerStrRight - = printSettings.footerStrLeft - = printSettings.footerStrCenter = ""; - printSettings.footerStrRight = "&D"; - - try { - webBrowserPrint.print(printSettings, null); - } catch (ex) { - // print()'s return codes are expressed as exceptions. Ignore. - } - }); - }, - - /** - * Save passphrase backup document to disk as HTML file. - * - * @param elid : ID of the form element containing the passphrase. - */ - passphraseSave: function(elid) { - let dialogTitle = this.bundle.GetStringFromName("save.recoverykey.title"); - let defaultSaveName = this.bundle.GetStringFromName("save.recoverykey.defaultfilename"); - this._preparePPiframe(elid, function(iframe) { - let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker); - let fpCallback = function fpCallback_done(aResult) { - if (aResult == Ci.nsIFilePicker.returnOK || - aResult == Ci.nsIFilePicker.returnReplace) { - let stream = Cc["@mozilla.org/network/file-output-stream;1"]. - createInstance(Ci.nsIFileOutputStream); - stream.init(fp.file, -1, PERMISSIONS_RWUSR, 0); - - let serializer = new XMLSerializer(); - let output = serializer.serializeToString(iframe.contentDocument); - output = output.replace(/<!DOCTYPE (.|\n)*?]>/, - '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" ' + - '"DTD/xhtml1-strict.dtd">'); - output = Weave.Utils.encodeUTF8(output); - stream.write(output, output.length); - } - }; - - fp.init(window, dialogTitle, Ci.nsIFilePicker.modeSave); - fp.appendFilters(Ci.nsIFilePicker.filterHTML); - fp.defaultString = defaultSaveName; - fp.open(fpCallback); - return false; - }); - }, - - /** - * validatePassword - * - * @param el1 : the first textbox element in the form - * @param el2 : the second textbox element, if omitted it's an update form - * - * returns [valid, errorString] - */ - validatePassword: function (el1, el2) { - let valid = false; - let val1 = el1.value; - let val2 = el2 ? el2.value : ""; - let error = ""; - - if (!el2) - valid = val1.length >= Weave.MIN_PASS_LENGTH; - else if (val1 && val1 == Weave.Service.identity.username) - error = "change.password.pwSameAsUsername"; - else if (val1 && val1 == Weave.Service.identity.account) - error = "change.password.pwSameAsEmail"; - else if (val1 && val1 == Weave.Service.identity.basicPassword) - error = "change.password.pwSameAsPassword"; - else if (val1 && val2) { - if (val1 == val2 && val1.length >= Weave.MIN_PASS_LENGTH) - valid = true; - else if (val1.length < Weave.MIN_PASS_LENGTH) - error = "change.password.tooShort"; - else if (val1 != val2) - error = "change.password.mismatch"; - } - let errorString = error ? Weave.Utils.getErrorString(error) : ""; - return [valid, errorString]; - } -}; |