Screenshots.jsm 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. "use strict";
  5. const EXPORTED_SYMBOLS = ["Screenshots"];
  6. Cu.importGlobalProperties(["fetch"]);
  7. ChromeUtils.defineModuleGetter(this, "BackgroundPageThumbs",
  8. "resource://gre/modules/BackgroundPageThumbs.jsm");
  9. ChromeUtils.defineModuleGetter(this, "PageThumbs",
  10. "resource://gre/modules/PageThumbs.jsm");
  11. ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
  12. "resource://gre/modules/PrivateBrowsingUtils.jsm");
  13. ChromeUtils.defineModuleGetter(this, "Services",
  14. "resource://gre/modules/Services.jsm");
  15. const GREY_10 = "#F9F9FA";
  16. this.Screenshots = {
  17. /**
  18. * Get a screenshot / thumbnail for a url. Either returns the disk cached
  19. * image or initiates a background request for the url.
  20. *
  21. * @param url {string} The url to get a thumbnail
  22. * @return {Promise} Resolves a custom object or null if failed
  23. */
  24. async getScreenshotForURL(url) {
  25. try {
  26. await BackgroundPageThumbs.captureIfMissing(url, {backgroundColor: GREY_10});
  27. const imgPath = PageThumbs.getThumbnailPath(url);
  28. const filePathResponse = await fetch(`file://${imgPath}`);
  29. const fileContents = await filePathResponse.blob();
  30. // Check if the file is empty, which indicates there isn't actually a
  31. // thumbnail, so callers can show a failure state.
  32. if (fileContents.size === 0) {
  33. return null;
  34. }
  35. return {path: imgPath, data: fileContents};
  36. } catch (err) {
  37. Cu.reportError(`getScreenshot(${url}) failed: ${err}`);
  38. }
  39. // We must have failed to get the screenshot, so persist the failure by
  40. // storing an empty file. Future calls will then skip requesting and return
  41. // failure, so do the same thing here. The empty file should not expire with
  42. // the usual filtering process to avoid repeated background requests, which
  43. // can cause unwanted high CPU, network and memory usage - Bug 1384094
  44. try {
  45. await PageThumbs._store(url, url, null, true);
  46. } catch (err) {
  47. // Probably failed to create the empty file, but not much more we can do.
  48. }
  49. return null;
  50. },
  51. /**
  52. * Checks if all the open windows are private browsing windows. If so, we do not
  53. * want to collect screenshots. If there exists at least 1 non-private window,
  54. * we are ok to collect screenshots.
  55. */
  56. _shouldGetScreenshots() {
  57. for (let win of Services.wm.getEnumerator("navigator:browser")) {
  58. if (!PrivateBrowsingUtils.isWindowPrivate(win)) {
  59. // As soon as we encounter 1 non-private window, screenshots are fair game.
  60. return true;
  61. }
  62. }
  63. return false;
  64. },
  65. /**
  66. * Conditionally get a screenshot for a link if there's no existing pending
  67. * screenshot. Updates the cached link's desired property with the result.
  68. *
  69. * @param link {object} Link object to update
  70. * @param url {string} Url to get a screenshot of
  71. * @param property {string} Name of property on object to set
  72. @ @param onScreenshot {function} Callback for when the screenshot loads
  73. */
  74. async maybeCacheScreenshot(link, url, property, onScreenshot) {
  75. // If there are only private windows open, do not collect screenshots
  76. if (!this._shouldGetScreenshots()) {
  77. return;
  78. }
  79. // Nothing to do if we already have a pending screenshot or
  80. // if a previous request failed and returned null.
  81. const cache = link.__sharedCache;
  82. if (cache.fetchingScreenshot || link[property] !== undefined) {
  83. return;
  84. }
  85. // Save the promise to the cache so other links get it immediately
  86. cache.fetchingScreenshot = this.getScreenshotForURL(url);
  87. // Clean up now that we got the screenshot
  88. const screenshot = await cache.fetchingScreenshot;
  89. delete cache.fetchingScreenshot;
  90. // Update the cache for future links and call back for existing content
  91. cache.updateLink(property, screenshot);
  92. onScreenshot(screenshot);
  93. },
  94. };