Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Noitidart/e8105a5f702dc9e6a4b8 to your computer and use it in GitHub Desktop.
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]
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);
});
@Noitidart
Copy link
Author

README

See Also GitHubGIST :: Noitidart / _ff-addon-snippet-ObjC_SwizzleWithExchangeImplementation.js
See Also GitHubGIST :: Noitidart / _ff-addon-snippet-ObjC_SetApplicationIconImage.js

Rev1

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:

    Screenshot demonstrating the effect of the swizzle, notice the icons have the "Beta" sash

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment