-
Notifications
You must be signed in to change notification settings - Fork 25
Modifying Firefox to Save PDF files automagically to MemoryCache
Modifying Firefox to Save PDF files automagically to MemoryCache by building from source.
If the pageSettings.silentMode
parameter is set to true when calling saveAsPDF
, we are bypassing opening up the file browser and asking the user to input a name and location for the file. This allows the extension to specify the subdirectory in the Downloads path, and then we use the print service to save there.
There's another API (downloads.download()
) that essentially performs this task, but it requires a file, rather than a URL, while the saveAsPDF
functionality can perform the HTML -> PDF conversion process. I chose to start with this approach since it was manageable for me, but in practice, there are considerations for implementing something like this at scale.
-
Follow the instructions from Mozilla Central to acquire a copy of the Firefox source code and build it. I'm using Ubuntu as my primary operating system, but you can build Firefox on Windows and MacOS.
-
Modify
tabs.json
to add a new property in thepageSettings
JSON properties:
"silentMode" : {
"type" : "boolean",
"optional" : true,
"description" : "Whether to silently save the PDF or open the file picker. Default: false"
}
For me, this was at line 408 after the property footerRight
was declared.
- Modify
Parent / ext-tabs.js
, replacing the code for thesaveAsPDF
API with the code that follows:
saveAsPDF(pageSettings) {
let activeTab = getTabOrActive(null);
console.log ("Silent mode: " + pageSettings.silentMode);
let filename;
if (
pageSettings.toFileName !== null &&
pageSettings.toFileName != ""
) {
filename = pageSettings.toFileName;
} else if (activeTab.linkedBrowser.contentTitle != "") {
filename = activeTab.linkedBrowser.contentTitle;
} else {
let url = new URL(activeTab.linkedBrowser.currentURI.spec);
let path = decodeURIComponent(url.pathname);
path = path.replace(/\/$/, "");
filename = path.split("/").pop();
if (filename == "") {
filename = url.hostname;
}
}
//filename = DownloadPaths.sanitize(filename);
if(!pageSettings.silentMode) {
let picker = Cc["@mozilla.org/filepicker;1"].createInstance(
Ci.nsIFilePicker
);
let title = strBundle.GetStringFromName(
"saveaspdf.saveasdialog.title"
);
picker.init(activeTab.ownerGlobal, title, Ci.nsIFilePicker.modeSave);
picker.appendFilter("PDF", "*.pdf");
picker.defaultExtension = "pdf";
picker.defaultString = filename;
return new Promise(resolve => {
picker.open(function (retval) {
if (retval == 0 || retval == 2) {
// OK clicked (retval == 0) or replace confirmed (retval == 2)
// Workaround: When trying to replace an existing file that is open in another application (i.e. a locked file),
// the print progress listener is never called. This workaround ensures that a correct status is always returned.
try {
let fstream = Cc[
"@mozilla.org/network/file-output-stream;1"
].createInstance(Ci.nsIFileOutputStream);
fstream.init(picker.file, 0x2a, 0o666, 0); // ioflags = write|create|truncate, file permissions = rw-rw-rw-
fstream.close();
} catch (e) {
resolve(retval == 0 ? "not_saved" : "not_replaced");
return;
}
let psService = Cc[
"@mozilla.org/gfx/printsettings-service;1"
].getService(Ci.nsIPrintSettingsService);
let printSettings = psService.createNewPrintSettings();
printSettings.printerName = "";
printSettings.isInitializedFromPrinter = true;
printSettings.isInitializedFromPrefs = true;
printSettings.outputDestination =
Ci.nsIPrintSettings.kOutputDestinationFile;
printSettings.toFileName = picker.file.path;
printSettings.printSilent = true;
printSettings.outputFormat =
Ci.nsIPrintSettings.kOutputFormatPDF;
if (pageSettings.paperSizeUnit !== null) {
printSettings.paperSizeUnit = pageSettings.paperSizeUnit;
}
if (pageSettings.paperWidth !== null) {
printSettings.paperWidth = pageSettings.paperWidth;
}
if (pageSettings.paperHeight !== null) {
printSettings.paperHeight = pageSettings.paperHeight;
}
if (pageSettings.orientation !== null) {
printSettings.orientation = pageSettings.orientation;
}
if (pageSettings.scaling !== null) {
printSettings.scaling = pageSettings.scaling;
}
if (pageSettings.shrinkToFit !== null) {
printSettings.shrinkToFit = pageSettings.shrinkToFit;
}
if (pageSettings.showBackgroundColors !== null) {
printSettings.printBGColors =
pageSettings.showBackgroundColors;
}
if (pageSettings.showBackgroundImages !== null) {
printSettings.printBGImages =
pageSettings.showBackgroundImages;
}
if (pageSettings.edgeLeft !== null) {
printSettings.edgeLeft = pageSettings.edgeLeft;
}
if (pageSettings.edgeRight !== null) {
printSettings.edgeRight = pageSettings.edgeRight;
}
if (pageSettings.edgeTop !== null) {
printSettings.edgeTop = pageSettings.edgeTop;
}
if (pageSettings.edgeBottom !== null) {
printSettings.edgeBottom = pageSettings.edgeBottom;
}
if (pageSettings.marginLeft !== null) {
printSettings.marginLeft = pageSettings.marginLeft;
}
if (pageSettings.marginRight !== null) {
printSettings.marginRight = pageSettings.marginRight;
}
if (pageSettings.marginTop !== null) {
printSettings.marginTop = pageSettings.marginTop;
}
if (pageSettings.marginBottom !== null) {
printSettings.marginBottom = pageSettings.marginBottom;
}
if (pageSettings.headerLeft !== null) {
printSettings.headerStrLeft = pageSettings.headerLeft;
}
if (pageSettings.headerCenter !== null) {
printSettings.headerStrCenter = pageSettings.headerCenter;
}
if (pageSettings.headerRight !== null) {
printSettings.headerStrRight = pageSettings.headerRight;
}
if (pageSettings.footerLeft !== null) {
printSettings.footerStrLeft = pageSettings.footerLeft;
}
if (pageSettings.footerCenter !== null) {
printSettings.footerStrCenter = pageSettings.footerCenter;
}
if (pageSettings.footerRight !== null) {
printSettings.footerStrRight = pageSettings.footerRight;
}
activeTab.linkedBrowser.browsingContext
.print(printSettings)
.then(() => resolve(retval == 0 ? "saved" : "replaced"))
.catch(() =>
resolve(retval == 0 ? "not_saved" : "not_replaced")
);
} else {
// Cancel clicked (retval == 1)
resolve("canceled");
}
});
});
}
else {
return new Promise(resolve => {
let psService = Cc[
"@mozilla.org/gfx/printsettings-service;1"
].getService(Ci.nsIPrintSettingsService);
let printSettings = psService.createNewPrintSettings();
printSettings.printerName = "";
printSettings.isInitializedFromPrinter = true;
printSettings.isInitializedFromPrefs = true;
printSettings.outputDestination = Ci.nsIPrintSettings.kOutputDestinationFile;
// For now, limit to downloads directory
printSettings.toFileName = "~/Downloads" + filename;
printSettings.printSilent = true;
printSettings.outputFormat = Ci.nsIPrintSettings.kOutputFormatPDF;
activeTab.linkedBrowser.browsingContext
.print(printSettings)
.then(() => resolve(retval == 0 ? "saved" : "replaced"))
.catch(() =>
resolve("error with silent printing"));
})
}
}
Rebuild an incremental build.