I am making a Share extension for all possible types. Now I am stuck with sharing from Safari. I'm using "NSExtensionJavaScriptPreprocessingFile" to get title, favicon and html of common pages. But this option changes type of attachment from "com.adobe.pdf" to "com.apple.property-list" without pdf content.
How can i get pdf version of webpage if NSExtensionJavaScriptPreprocessingFile is set?
214 Views Asked by Nik.206 At
2
There are 2 best solutions below
1

Dropbox sets it to a JS file with the following code:
var WebSaverExtension = function() {};
function db_checkDefined(object) {
return (typeof object !== "undefined") && (object !== null);
}
var db_baseURL = "";
var db_documentHTML = "";
var db_title = "";
var db_height = -1;
var db_width = -1;
var db_URL = "";
// This is used for unit tests. This must point to the document object that this script alters.
var unitTest_DBWebSaverSecurityTests_updatedDocument = "";
if (db_checkDefined(window) && db_checkDefined(window.location)) {
db_URL = window.location.href;
}
if (db_checkDefined(document)) {
// get width and height of element
// documentClone doesn't have body defined so we get the body straight from the document
if (db_checkDefined(document.body)) {
db_height = document.body.scrollHeight > 0 ? document.body.scrollHeight : window.innerHeight;
db_width = document.body.scrollWidth;
}
// get title
if (db_checkDefined(document.title)) {
db_title = document.title;
}
// get base url
var baseArray = document.getElementsByTagName('base');
if (baseArray.length != 0) {
db_baseURL = baseArray[0].href;
} else {
var href = db_URL;
var index = href.lastIndexOf("/");
if (index == -1) {
db_baseURL = href;
} else {
db_baseURL = href.substring(0, index + 1);
}
}
// documentElement should always exist for a html file
if (db_checkDefined(document.documentElement)) {
var documentClone = document.cloneNode(true);
// for unit tests to test this document object
unitTest_DBWebSaverSecurityTests_updatedDocument = documentClone;
// set the image sizes so they don't change size when we reload them
// getElementsByTagName returns nodes in the order in which they would be encountered in a
// preorder traversal of the Document tree.
var images = document.documentElement.getElementsByTagName('img');
// don't bother. This will be a timeout
if (images.length <= 10000) {
var imagesCloned = documentClone.documentElement.getElementsByTagName('img');
for (var i = images.length-1; i >= 0; i--) {
imagesCloned[i].style.height = images[i].scrollHeight + "px";
imagesCloned[i].style.width = images[i].scrollWidth + "px";
}
}
var tags = [ "header", "div", "span", "footer", "iframe" ];
for (var iTag = 0; iTag < tags.length; iTag++) {
var tag = tags[iTag];
var elements = document.documentElement.getElementsByTagName(tag);
// don't bother. This will be a timeout
if (elements.length > 10000) {
continue;
}
var elementsCloned = documentClone.documentElement.getElementsByTagName(tag);
for (var i = elements.length-1; i >= 0; i--) {
var element = elements[i];
var elementClone = elementsCloned[i];
if (db_checkDefined(element) && db_checkDefined(elementClone) && db_checkDefined(element.style)) {
var height = window.getComputedStyle(element).getPropertyValue('height');
if (db_checkDefined(height)) {
var minHeight = window.getComputedStyle(element).getPropertyValue('min-height');
if (db_checkDefined(minHeight)) {
elementClone.style.minHeight = height;
}
elementClone.style.height = height;
var maxHeight = window.getComputedStyle(element).getPropertyValue('max-height');
if (db_checkDefined(maxHeight)) {
elementClone.style.maxHeight = height;
}
}
var width = window.getComputedStyle(element).getPropertyValue('width');
if (db_checkDefined(width)) {
var minWidth = window.getComputedStyle(element).getPropertyValue('min-width');
if (db_checkDefined(minWidth)) {
elementClone.style.minWidth = width;
}
}
}
}
}
// remove script tags
var scripts = documentClone.documentElement.getElementsByTagName('script');
for (var i = scripts.length-1; i >= 0; i--) {
scripts[i].parentNode.removeChild(scripts[i]);
}
// disable scripts in iframes (can cause Javascript selected ads to not be shown)
// we can't access the html in the iframe so we have to live with it being reloaded but we can prevent
// Javascript from being executed again
var iframes = documentClone.documentElement.getElementsByTagName('iframe');
for (var i = iframes.length-1; i >= 0; i--) {
var iframe = iframes[i];
iframe.sandbox = true;
}
// Add csp policy to prevent script loading
// safari should auto create head if the html is missing it
// this is here for safety
var head = documentClone.head;
if (!db_checkDefined(head)) {
// make a head
head = documentClone.createElement('head');
// if firstchild is null, insertBefore adds to end of children list
documentClone.documentElement.insertBefore(head, documentClone.documentElement.firstChild);
}
// http://www.w3.org/TR/CSP2/#delivery-html-meta-element
// Note: Having multiple policies is ok
var metaElement = documentClone.createElement('meta');
metaElement.httpEquiv = 'Content-Security-Policy';
metaElement.content = "script-src 'none'";
// if firstchild is null, insertBefore adds to end of children list
head.insertBefore(metaElement, head.firstChild);
// get the changed html
if (db_checkDefined(documentClone.documentElement.outerHTML)) {
db_documentHTML = documentClone.documentElement.outerHTML;
}
}
}
WebSaverExtension.prototype = {
run: function(arguments) {
arguments.completionFunction({"baseURL": db_baseURL,
"URL": db_URL,
"html": db_documentHTML,
"title": db_title,
"height": db_height,
"width": db_width});
},
finalize: function(arguments) {
}
}
var ExtensionPreprocessingJS = new WebSaverExtension;
It nicely creates a PDF file out of the web page in Safari, but just creates a .url file when used in Chrome.
After taking some more insight I think I can safely say that this is a bug in Safari.
Safari sends only
NSData
that has 135 bytes. It can be converted to the property list that appears asNSDictionary
, but it has only 4 keys that don't appear to hold any useful information ("version", "archiver", "objects" - it isnil
, "top").Developers can report it to Apple using the Feedback Assistant app.