]> git.saurik.com Git - winterboard.git/blob - WBMarkup.mm
On iOS 7, we can't always rely on libhide working.
[winterboard.git] / WBMarkup.mm
1 #include "WBMarkup.h"
2
3 #include <substrate.h>
4
5 @class WKView;
6
7 extern "C" void WebThreadLock();
8 extern "C" CGContextRef WKGetCurrentGraphicsContext();
9
10 static void (*WKViewLockFocus$)(WKView *);
11 static void (*WKViewUnlockFocus$)(WKView *);
12 static void (*WKViewDisplayRect$)(WKView *, CGRect);
13
14 @interface DOMElement : NSObject
15 - (void) setInnerHTML:(NSString *)value;
16 - (void) setInnerText:(NSString *)value;
17 - (void) setAttribute:(NSString *)name value:(NSString *)value;
18 - (void) removeAttribute:(NSString *)name;
19 - (DOMElement *) firstChild;
20 - (void) appendChild:(DOMElement *)child;
21 - (void) removeChild:(DOMElement *)child;
22 - (void) setScrollXOffset:(float)x scrollYOffset:(float)y;
23 @end
24
25 @interface DOMDocument : NSObject
26 - (DOMElement *) getElementById:(NSString *)id;
27 @end
28
29 @interface WebPreferences : NSObject
30 - (id) initWithIdentifier:(NSString *)identifier;
31 - (void) setPlugInsEnabled:(BOOL)value;
32 @end
33
34 @interface WebFrameView : NSObject
35 - (void) setAllowsScrolling:(BOOL)value;
36 @end
37
38 @interface WebFrame : NSObject
39 - (WebFrameView *) frameView;
40 - (void) _setLoadsSynchronously:(BOOL)value;
41 - (void) loadHTMLString:(NSString *)string baseURL:(id)url;
42 - (void) forceLayoutAdjustingViewSize:(BOOL)adjust;
43 - (CGSize) renderedSizeOfNode:(DOMElement *)node constrainedToWidth:(float)width;
44 - (DOMDocument *) DOMDocument;
45 @end
46
47 @interface WAKView : NSObject
48 - (void) _drawRect:(CGRect)rect context:(CGContext *)context lockFocus:(bool)focus;
49 @end
50
51 @interface WebView : WAKView
52 - (id) initWithFrame:(CGRect)frame;
53 - (WebFrame *) mainFrame;
54 - (void) setDrawsBackground:(BOOL)value;
55 - (void) setPreferences:(WebPreferences *)preferences;
56 - (WKView *) _viewRef;
57 @end
58
59 @interface WAKWindow : NSObject
60 - (id) initWithFrame:(CGRect)frame;
61 - (void) setContentView:(WebView *)view;
62 @end
63
64 static WBMarkup *SharedMarkup_;
65
66 @implementation WBMarkup
67
68 + (void) initialize {
69 MSImageRef WebCore(MSGetImageByName("/System/Library/PrivateFrameworks/WebCore.framework/WebCore"));
70 MSHookSymbol(WKViewLockFocus$, "WKViewLockFocus", WebCore);
71 MSHookSymbol(WKViewUnlockFocus$, "WKViewUnlockFocus", WebCore);
72 MSHookSymbol(WKViewDisplayRect$, "WKViewDisplayRect", WebCore);
73 }
74
75 + (BOOL) isSharedMarkupCreated {
76 return SharedMarkup_ != nil;
77 }
78
79 + (WBMarkup *) sharedMarkup {
80 if (SharedMarkup_ == nil)
81 SharedMarkup_ = [[WBMarkup alloc] init];
82 return SharedMarkup_;
83 }
84
85 - (id) init {
86 if ((self = [super init]) != nil) {
87 WebThreadLock();
88
89 SharedMarkup_ = self;
90
91 view_ = [[WebView alloc] initWithFrame:CGRectMake(0, 0, 640, 5000)];
92 [view_ setDrawsBackground:NO];
93
94 WebPreferences *preferences([[WebPreferences alloc] initWithIdentifier:@"com.apple.webkit.webmarkup"]);
95 [preferences setPlugInsEnabled:NO];
96 [view_ setPreferences:preferences];
97 [preferences release];
98
99 window_ = [[WAKWindow alloc] initWithFrame:CGRectMake(0, 0, 640, 5000)];
100 [window_ setContentView:view_];
101
102 WebFrame *frame([view_ mainFrame]);
103 [[frame frameView] setAllowsScrolling:NO];
104 [frame _setLoadsSynchronously:YES];
105
106 [frame loadHTMLString:@"<html><body style='margin: 0px; word-wrap: break-word; -khtml-nbsp-mode: space; -khtml-line-break: after-white-space'><div id='size'><div id='text'></div></div></body></html>" baseURL:nil];
107 } return self;
108 }
109
110 - (void) dealloc {
111 [window_ release];
112 [view_ release];
113 [super dealloc];
114 }
115
116 - (void) drawRect:(CGRect)rect {
117 [text_ setScrollXOffset:origin_.x scrollYOffset:origin_.y];
118
119 CGRect draw(CGRectMake(0, 0, rect.size.width - rect.origin.x, rect.size.height - rect.origin.y));
120
121 CGContextSaveGState(context_); {
122 CGContextTranslateCTM(context_, rect.origin.x, rect.origin.y);
123
124 if (kCFCoreFoundationVersionNumber > 700)
125 [view_ _drawRect:draw context:context_ lockFocus:YES];
126 else {
127 WKView *view([view_ _viewRef]);
128 WKViewLockFocus$(view); {
129 WKViewDisplayRect$(view, draw);
130 } WKViewUnlockFocus$(view);
131 }
132 } CGContextRestoreGState(context_);
133 }
134
135 - (WebView *) _webView {
136 return view_;
137 }
138
139 - (void) setStringDrawingOrigin:(CGPoint)origin {
140 origin_ = origin;
141 }
142
143 - (void) clearStringDrawingOrigin {
144 origin_ = CGPointZero;
145 }
146
147 - (CGSize) sizeOfMarkup:(NSString *)markup forWidth:(float)width {
148 WebThreadLock();
149
150 if (![self _webPrepareContextForTextDrawing:NO])
151 return CGSizeZero;
152
153 [text_ setInnerHTML:markup];
154 [text_ removeAttribute:@"style"];
155
156 NSString *value([[NSString alloc] initWithFormat:[self _styleFormatString:@"width: %.0fpx; height: 5000px"], width]);
157 [size_ setAttribute:@"style" value:value];
158 [value release];
159
160 [[view_ mainFrame] forceLayoutAdjustingViewSize:YES];
161 return [[view_ mainFrame] renderedSizeOfNode:text_ constrainedToWidth:width];
162 }
163
164 - (CGSize) sizeOfString:(NSString *)string withStyle:(NSString *)style forWidth:(float)width {
165 WebThreadLock();
166
167 if (![self _webPrepareContextForTextDrawing:NO])
168 return CGSizeZero;
169
170 [size_ removeChild:[size_ firstChild]];
171
172 WebFrame *frame([view_ mainFrame]);
173
174 [frame forceLayoutAdjustingViewSize:YES];
175 [text_ setInnerText:string];
176 [self _setupWithStyle:style width:width height:5000];
177 [frame forceLayoutAdjustingViewSize:YES];
178
179 return [[view_ mainFrame] renderedSizeOfNode:text_ constrainedToWidth:width];
180 }
181
182 - (NSString *) _styleFormatString:(NSString *)style {
183 return style;
184 }
185
186 - (void) _setupWithStyle:(NSString *)style width:(float)width height:(float)height {
187 WebThreadLock();
188
189 if (style != nil && [style length] != 0)
190 [text_ setAttribute:@"style" value:style];
191 else
192 [text_ removeAttribute:@"style"];
193
194 NSString *value([[NSString alloc] initWithFormat:[self _styleFormatString:@"width: %.0fpx; height: %.0fpx"], width, height]);
195 [size_ setAttribute:@"style" value:value];
196 [value release];
197
198 [size_ appendChild:text_];
199 }
200
201 - (BOOL) _webPrepareContextForTextDrawing:(BOOL)drawing {
202 WebThreadLock();
203
204 if (document_ == nil) {
205 WebFrame *frame([view_ mainFrame]);
206
207 document_ = [[frame DOMDocument] retain];
208 if (document_ == nil) {
209 NSLog(@"*** ERROR: no DOM document in text-drawing webview");
210 return NO;
211 }
212
213 text_ = [[document_ getElementById:@"text"] retain];
214 size_ = [[document_ getElementById:@"size"] retain];
215
216 if (text_ == nil || size_ == nil) {
217 NSLog(@"*** ERROR: cannot find DOM element required for text drawing");
218 return NO;
219 }
220 }
221
222 context_ = NULL;
223
224 if (!drawing)
225 context_ = NULL;
226 else {
227 context_ = WKGetCurrentGraphicsContext();
228 if (context_ == NULL) {
229 NSLog(@"*** ERROR: no CGContext set for drawing");
230 return NO;
231 }
232 }
233
234 return YES;
235 }
236
237 - (void) drawMarkup:(NSString *)markup atPoint:(CGPoint)point {
238 [self drawMarkup:markup inRect:CGRectMake(point.x, point.y, 65535, 65535)];
239 }
240
241 - (void) drawMarkup:(NSString *)markup inRect:(CGRect)rect {
242 WebThreadLock();
243
244 if (![self _webPrepareContextForTextDrawing:YES])
245 return;
246
247 [text_ setInnerHTML:markup];
248 [text_ removeAttribute:@"style"];
249
250 NSString *value([[NSString alloc] initWithFormat:[self _styleFormatString:@"width: %.0fpx; height: %.0fpx"], CGRectGetWidth(rect), CGRectGetHeight(rect)]);
251 [size_ setAttribute:@"style" value:value];
252 [value release];
253
254 [[view_ mainFrame] forceLayoutAdjustingViewSize:YES];
255
256 [self drawRect:rect];
257 }
258
259 - (void) drawString:(NSString *)string atPoint:(CGPoint)point withStyle:(NSString *)style {
260 [self drawString:string inRect:CGRectMake(point.x, point.y, 65535, 65535) withStyle:style];
261 }
262
263 - (void) drawString:(NSString *)string inRect:(CGRect)rect withStyle:(NSString *)style {
264 WebThreadLock();
265
266 if (![self _webPrepareContextForTextDrawing:YES])
267 return;
268
269 [size_ removeChild:[size_ firstChild]];
270
271 WebFrame *frame([view_ mainFrame]);
272
273 [frame forceLayoutAdjustingViewSize:YES];
274 [text_ setInnerText:string];
275 [self _setupWithStyle:style width:CGRectGetWidth(rect) height:CGRectGetHeight(rect)];
276 [frame forceLayoutAdjustingViewSize:YES];
277
278 [self drawRect:rect];
279 }
280
281 @end