tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

PromptCollection.sys.mjs (8639B)


      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 
      5 const lazy = {};
      6 ChromeUtils.defineESModuleGetters(lazy, {
      7  SelectableProfileService:
      8    "resource:///modules/profiles/SelectableProfileService.sys.mjs",
      9 });
     10 
     11 /**
     12 * Implements nsIPromptCollection
     13 *
     14 * @class PromptCollection
     15 */
     16 export class PromptCollection {
     17  confirmRepost(browsingContext) {
     18    let brandName;
     19    try {
     20      brandName = this.stringBundles.brand.GetStringFromName("brandShortName");
     21    } catch (exception) {
     22      // That's ok, we'll use a generic version of the prompt
     23    }
     24 
     25    let message;
     26    let resendLabel;
     27    try {
     28      if (brandName) {
     29        message = this.stringBundles.app.formatStringFromName(
     30          "confirmRepostPrompt",
     31          [brandName]
     32        );
     33      } else {
     34        // Use a generic version of this prompt.
     35        message = this.stringBundles.app.GetStringFromName(
     36          "confirmRepostPrompt"
     37        );
     38      }
     39      resendLabel =
     40        this.stringBundles.app.GetStringFromName("resendButton.label");
     41    } catch (exception) {
     42      console.error("Failed to get strings from appstrings.properties");
     43      return false;
     44    }
     45 
     46    let docViewer = browsingContext?.docShell?.docViewer;
     47    let modalType = docViewer?.isTabModalPromptAllowed
     48      ? Ci.nsIPromptService.MODAL_TYPE_CONTENT
     49      : Ci.nsIPromptService.MODAL_TYPE_WINDOW;
     50    let buttonFlags =
     51      (Ci.nsIPromptService.BUTTON_TITLE_IS_STRING *
     52        Ci.nsIPromptService.BUTTON_POS_0) |
     53      (Ci.nsIPromptService.BUTTON_TITLE_CANCEL *
     54        Ci.nsIPromptService.BUTTON_POS_1);
     55    let buttonPressed = Services.prompt.confirmExBC(
     56      browsingContext,
     57      modalType,
     58      null,
     59      message,
     60      buttonFlags,
     61      resendLabel,
     62      null,
     63      null,
     64      null,
     65      {}
     66    );
     67 
     68    return buttonPressed === 0;
     69  }
     70 
     71  async asyncBeforeUnloadCheck(browsingContext) {
     72    const docViewer = browsingContext?.docShell?.docViewer;
     73    if (
     74      (docViewer && !docViewer.isTabModalPromptAllowed) ||
     75      !browsingContext.ancestorsAreCurrent
     76    ) {
     77      console.error("Can't prompt from inactive content viewer");
     78      return true;
     79    }
     80 
     81    let originNoSuffix =
     82      browsingContext.embedderElement?.contentPrincipal.originNoSuffix;
     83    const isPDFjs = originNoSuffix === "resource://pdf.js";
     84    const isProfilePage =
     85      originNoSuffix === "about:newprofile" ||
     86      originNoSuffix === "about:editprofile";
     87 
     88    let title, message, leaveLabel, stayLabel, buttonFlags;
     89    let args = {
     90      // Tell the prompt service that this is a permit unload prompt
     91      // so that it can set the appropriate flag on the detail object
     92      // of the events it dispatches.
     93      inPermitUnload: true,
     94    };
     95 
     96    try {
     97      if (isPDFjs) {
     98        title = this.stringBundles.dom.GetStringFromName(
     99          "OnBeforeUnloadPDFjsTitle"
    100        );
    101        message = this.stringBundles.dom.GetStringFromName(
    102          "OnBeforeUnloadPDFjsMessage"
    103        );
    104        buttonFlags =
    105          Ci.nsIPromptService.BUTTON_POS_0_DEFAULT |
    106          (Ci.nsIPrompt.BUTTON_TITLE_SAVE * Ci.nsIPrompt.BUTTON_POS_0) |
    107          (Ci.nsIPrompt.BUTTON_TITLE_CANCEL * Ci.nsIPrompt.BUTTON_POS_1) |
    108          (Ci.nsIPrompt.BUTTON_TITLE_DONT_SAVE * Ci.nsIPrompt.BUTTON_POS_2);
    109        args.useTitle = true;
    110        args.headerIconCSSValue =
    111          "url('chrome://branding/content/document_pdf.svg')";
    112      } else if (isProfilePage) {
    113        title = this.stringBundles.dom.GetStringFromName(
    114          "OnBeforeUnloadAboutNewProfileTitle"
    115        );
    116        let defaultName = lazy.SelectableProfileService.currentProfile.name;
    117        message = this.stringBundles.dom.formatStringFromName(
    118          "OnBeforeUnloadAboutNewProfileMessage",
    119          [defaultName]
    120        );
    121        leaveLabel = this.stringBundles.dom.GetStringFromName(
    122          "OnBeforeUnloadAboutNewProfileLeaveButton"
    123        );
    124        stayLabel = this.stringBundles.dom.GetStringFromName(
    125          "OnBeforeUnloadAboutNewProfileStayButton"
    126        );
    127        buttonFlags =
    128          Ci.nsIPromptService.BUTTON_POS_0_DEFAULT |
    129          (Ci.nsIPromptService.BUTTON_TITLE_IS_STRING *
    130            Ci.nsIPromptService.BUTTON_POS_0) |
    131          (Ci.nsIPromptService.BUTTON_TITLE_IS_STRING *
    132            Ci.nsIPromptService.BUTTON_POS_1);
    133        args.useTitle = true;
    134      } else {
    135        title = this.stringBundles.dom.GetStringFromName("OnBeforeUnloadTitle");
    136        message = this.stringBundles.dom.GetStringFromName(
    137          "OnBeforeUnloadMessage2"
    138        );
    139        leaveLabel = this.stringBundles.dom.GetStringFromName(
    140          "OnBeforeUnloadLeaveButton"
    141        );
    142        stayLabel = this.stringBundles.dom.GetStringFromName(
    143          "OnBeforeUnloadStayButton"
    144        );
    145        buttonFlags =
    146          Ci.nsIPromptService.BUTTON_POS_0_DEFAULT |
    147          (Ci.nsIPromptService.BUTTON_TITLE_IS_STRING *
    148            Ci.nsIPromptService.BUTTON_POS_0) |
    149          (Ci.nsIPromptService.BUTTON_TITLE_IS_STRING *
    150            Ci.nsIPromptService.BUTTON_POS_1);
    151      }
    152    } catch (exception) {
    153      console.error("Failed to get strings from dom.properties");
    154      return false;
    155    }
    156 
    157    const result = await Services.prompt.asyncConfirmEx(
    158      browsingContext,
    159      Services.prompt.MODAL_TYPE_CONTENT,
    160      title,
    161      message,
    162      buttonFlags,
    163      leaveLabel,
    164      stayLabel,
    165      null,
    166      null,
    167      false,
    168      args
    169    );
    170    const buttonNumClicked = result
    171      .QueryInterface(Ci.nsIPropertyBag2)
    172      .get("buttonNumClicked");
    173    if (isPDFjs) {
    174      if (buttonNumClicked === 0) {
    175        const savePdfPromise = new Promise(resolve => {
    176          Services.obs.addObserver(
    177            {
    178              observe(_aSubject, aTopic) {
    179                if (aTopic === "pdfjs:saveComplete") {
    180                  Services.obs.removeObserver(this, aTopic);
    181                  resolve();
    182                }
    183              },
    184            },
    185            "pdfjs:saveComplete"
    186          );
    187        });
    188        const actor = browsingContext.currentWindowGlobal.getActor("Pdfjs");
    189        actor.sendAsyncMessage("PDFJS:Save");
    190        await savePdfPromise;
    191      }
    192      return buttonNumClicked !== 1;
    193    } else if (isProfilePage) {
    194      if (buttonNumClicked === 0) {
    195        Services.prefs.setBoolPref(
    196          "browser.profiles.profile-name.updated",
    197          true
    198        );
    199      }
    200      let gleanFn =
    201        originNoSuffix === "about:newprofile"
    202          ? "profilesNew"
    203          : "profilesExisting";
    204      let value = buttonNumClicked === 0 ? "leave" : "cancel";
    205      Glean[gleanFn].alert.record({ value });
    206 
    207      return buttonNumClicked !== 1;
    208    }
    209 
    210    return buttonNumClicked === 0;
    211  }
    212 
    213  confirmFolderUpload(browsingContext, directoryName) {
    214    let title;
    215    let message;
    216    let acceptLabel;
    217 
    218    try {
    219      title = this.stringBundles.dom.GetStringFromName(
    220        "FolderUploadPrompt.title"
    221      );
    222      message = this.stringBundles.dom.formatStringFromName(
    223        "FolderUploadPrompt.message",
    224        [directoryName]
    225      );
    226      acceptLabel = this.stringBundles.dom.GetStringFromName(
    227        "FolderUploadPrompt.acceptButtonLabel"
    228      );
    229    } catch (exception) {
    230      console.error("Failed to get strings from dom.properties");
    231      return false;
    232    }
    233 
    234    let buttonFlags =
    235      Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0 +
    236      Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1 +
    237      Services.prompt.BUTTON_POS_1_DEFAULT;
    238 
    239    return (
    240      Services.prompt.confirmExBC(
    241        browsingContext,
    242        Services.prompt.MODAL_TYPE_TAB,
    243        title,
    244        message,
    245        buttonFlags | Ci.nsIPrompt.BUTTON_DELAY_ENABLE,
    246        acceptLabel,
    247        null,
    248        null,
    249        null,
    250        {}
    251      ) === 0
    252    );
    253  }
    254 }
    255 
    256 const BUNDLES = {
    257  dom: "chrome://global/locale/dom/dom.properties",
    258  app: "chrome://global/locale/appstrings.properties",
    259  brand: "chrome://branding/locale/brand.properties",
    260 };
    261 
    262 PromptCollection.prototype.stringBundles = {};
    263 
    264 for (const [bundleName, bundleUrl] of Object.entries(BUNDLES)) {
    265  ChromeUtils.defineLazyGetter(
    266    PromptCollection.prototype.stringBundles,
    267    bundleName,
    268    function () {
    269      let bundle = Services.strings.createBundle(bundleUrl);
    270      if (!bundle) {
    271        throw new Error("String bundle for dom not present!");
    272      }
    273      return bundle;
    274    }
    275  );
    276 }
    277 
    278 PromptCollection.prototype.QueryInterface = ChromeUtils.generateQI([
    279  "nsIPromptCollection",
    280 ]);