Last active
August 29, 2015 14:14
-
-
Save Noitidart/e8105a5f702dc9e6a4b8 to your computer and use it in GitHub Desktop.
_ff-addon-snippet-ObjC_SwizzleWithSetImplementation - Demonstrates how to swizzle with js-ctypes through method_setImplementation by making [NSImage imageNamed] take my custom icon whenever it asks for @'NSApplicationIcon'. [objectivec] [jsctypes] [osx]
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Cu.import('resource://gre/modules/osfile.jsm'); | |
Cu.import('resource://gre/modules/ctypes.jsm'); | |
var objc = ctypes.open(ctypes.libraryName('objc')); | |
/** START - edit these **/ | |
var jsStr_imagePath = OS.Path.join(OS.Constants.Path.desktopDir, 'ff-logos', 'beta48.png'); | |
/** END - edit these **/ | |
// BASIC TYPES | |
var CHAR = ctypes.char; | |
var CLASS = ctypes.voidptr_t; | |
var ID = ctypes.voidptr_t; | |
var SEL = ctypes.voidptr_t; | |
var BOOL = ctypes.signed_char; | |
var NSUINTEGER = ctypes.unsigned_long; | |
var METHOD = ctypes.voidptr_t; | |
// ADVANCED TYPES | |
var IMP_for_imageNamed = ctypes.FunctionType(ctypes.default_abi, ID, [ID, SEL, ID]).ptr; //repalced variadic with ID as its specific to my use otherwise doing class_addMethod throws error saying expected pointer blah blah //ctypes.FunctionType(ctypes.default_abi, ID, [ID, SEL, '...']).ptr; | |
// COMMON FUNCTIONS | |
var class_getClassMethod = objc.declare('class_getClassMethod', ctypes.default_abi, METHOD, CLASS, SEL); | |
var method_setImplementation = objc.declare('method_setImplementation', ctypes.default_abi, IMP_for_imageNamed, METHOD, IMP_for_imageNamed); | |
var objc_getClass = objc.declare('objc_getClass', ctypes.default_abi, ID, CHAR.ptr); | |
var objc_msgSend = objc.declare('objc_msgSend', ctypes.default_abi, ID, ID, SEL, '...'); | |
var sel_registerName = objc.declare('sel_registerName', ctypes.default_abi, SEL, CHAR.ptr); | |
// COMMON SELECTORS | |
var alloc = sel_registerName('alloc'); | |
var init = sel_registerName('init'); | |
var release = sel_registerName('release'); | |
// my personal globals for this code | |
var myIcon; // global so i can do release on it at shutdown | |
var swizzled_imageNamed; // important to make this global otherwise the callback will be GC'ed and firefox will crash | |
function shutdown() { | |
//put code here to unswizzle it | |
if (myIcon) { | |
objc_msgSend(myIcon, release); | |
} | |
objc.close(); | |
}; | |
var promise_makeMyNSImage = OS.File.read(jsStr_imagePath); | |
promise_makeMyNSImage.then( | |
function(iconData) { | |
// NOTE: iconData is Uint8Array | |
var length = NSUINTEGER(iconData.length); | |
var bytes = ctypes.uint8_t.array()(iconData); | |
// data = [NSData dataWithBytes: bytes length: length]; | |
var NSData = objc_getClass('NSData'); | |
var dataWithBytes_length = sel_registerName('dataWithBytes:length:'); | |
var data = objc_msgSend(NSData, dataWithBytes_length, bytes, length); | |
// myIcon = [[NSImage alloc] initWithData: data]; | |
var NSImage = objc_getClass('NSImage'); | |
var initWithData = sel_registerName('initWithData:'); | |
myIcon = objc_msgSend(objc_msgSend(NSImage, alloc), initWithData, data); | |
if (myIcon.isNull()) { | |
throw new Error('Image file is corrupted. Will not continue to swizzle.'); | |
} | |
var UTF8String = sel_registerName('UTF8String'); | |
var originalMethod; | |
function js_swizzled_imageNamed(c_arg1__self, c_arg2__sel, objc_arg1__NSStringPtr) { | |
console.log('SWIZZLED: imageNamed called'); | |
var tt_read = objc_msgSend(objc_arg1__NSStringPtr, UTF8String); | |
console.info('tt_read:', tt_read, tt_read.toString(), uneval(tt_read), tt_read.isNull()); | |
var tt_read_casted = ctypes.cast(tt_read, CHAR.ptr); | |
console.info('tt_read_casted:', tt_read_casted, tt_read_casted.toString(), uneval(tt_read_casted), tt_read_casted.isNull()); | |
var tt_read_jsStr = tt_read_casted.readStringReplaceMalformed(); | |
console.info('tt_read_jsStr:', tt_read_jsStr, tt_read_jsStr.toString(), uneval(tt_read_jsStr)); // TypeError: tt_read_jsStr.isNull is not a function | |
if (tt_read_jsStr == 'NSApplicationIcon') { | |
// do my hook | |
console.log('this is purpose of this swizzle, to return myIcon when they ask for NSApplicationicon'); | |
return myIcon; | |
} else { | |
// do normal | |
console.log('doing regular imageNamed'); | |
var icon = originalMethod(c_arg1__self, c_arg2__sel, objc_arg1__NSStringPtr); // this is how you call the original | |
return icon; | |
} | |
} | |
swizzled_imageNamed = IMP_for_imageNamed(js_swizzled_imageNamed); //if use IMP as non-specifically defined as `ctypes.FunctionType(ctypes.default_abi, ID, [ID, SEL, '...']).ptr` you will have variadic in callback defined above, it keeps throwing expecting pointer blah blah. and it wouldnt accept me putting in variadic on this line if do use varidic, on this line it throws `Can't delcare a variadic callback function` | |
var imageNamed = sel_registerName('imageNamed:'); | |
var method = class_getClassMethod(NSImage, imageNamed); | |
originalMethod = method_setImplementation(method, swizzled_imageNamed); | |
console.info('originalMethod:', originalMethod, originalMethod.toString(), uneval(originalMethod)); | |
if (originalMethod.isNull()) { | |
throw new Error('originalMethod is null so swizzle failed'); | |
} else { | |
console.log('SUCCESFULLY SWIZZLED'); | |
} | |
}, | |
function(aReason) { | |
var rejObj = { | |
nameOfRejector: 'promise_makeMyNSImage', | |
aReason: aReason | |
}; | |
console.warn(rejObj); | |
throw rejObj; | |
} | |
).catch(function(aCaught) { | |
shutdown(); | |
console.error('promise_makeMyNSImage Catch:', aCaught); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
README
See Also GitHubGIST :: Noitidart / _ff-addon-snippet-ObjC_SwizzleWithExchangeImplementation.js
See Also GitHubGIST :: Noitidart / _ff-addon-snippet-ObjC_SetApplicationIconImage.js
Rev1
IMP
not as the general way (ctypes.FunctionType(ctypes.default_abi, ID, [ID, SEL, '...']).ptr
, you have to use specific to what your callback is. You cannot have a variadic (...
) in callback apparentlyswizzled_imageNamed
must be globally declared, otherwise it will get GC'ed within seconds and Firefox will crash.Rev2
Code had typo, fixed it
Works, can copy and paste and run from scratchpad, but you need a folder on your desktop called "ff-logos" with a png inside it called "beta48.png"
To see the swizzling in action, download an image, or open scratchpad and save file as blah.js and then do save as of blah.js again this makes it show dialog prompt you will see the swizzled image. Both cases are shown in screenshot here, notice the icons have the "Beta" sash: