]>
Commit | Line | Data |
---|---|---|
2de63e63 JF |
1 | #include "WBMarkup.h" |
2 | ||
fdac1738 JF |
3 | #include <substrate.h> |
4 | ||
2de63e63 JF |
5 | @class WKView; |
6 | ||
fdac1738 JF |
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); | |
2de63e63 JF |
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 | ||
09ea4166 JF |
47 | @interface WAKView : NSObject |
48 | - (void) _drawRect:(CGRect)rect context:(CGContext *)context lockFocus:(bool)focus; | |
49 | @end | |
50 | ||
51 | @interface WebView : WAKView | |
2de63e63 JF |
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 | ||
fdac1738 JF |
68 | + (void) initialize { |
69 | MSImageRef WebCore(MSGetImageByName("/System/Library/PrivateFrameworks/WebCore.framework/WebCore")); | |
3925268e JF |
70 | MSHookSymbol(WKViewLockFocus$, "_WKViewLockFocus", WebCore); |
71 | MSHookSymbol(WKViewUnlockFocus$, "_WKViewUnlockFocus", WebCore); | |
72 | MSHookSymbol(WKViewDisplayRect$, "_WKViewDisplayRect", WebCore); | |
67daf503 JF |
73 | |
74 | MSImageRef JavaScriptCore(MSGetImageByName("/System/Library/Frameworks/JavaScriptCore.framework/JavaScriptCore")); | |
75 | ||
76 | void (*_ZN3JSC19initializeThreadingEv)(); | |
77 | MSHookSymbol(_ZN3JSC19initializeThreadingEv, "__ZN3JSC19initializeThreadingEv", JavaScriptCore); | |
78 | if (_ZN3JSC19initializeThreadingEv != NULL) | |
79 | (*_ZN3JSC19initializeThreadingEv)(); | |
fdac1738 JF |
80 | } |
81 | ||
2de63e63 JF |
82 | + (BOOL) isSharedMarkupCreated { |
83 | return SharedMarkup_ != nil; | |
84 | } | |
85 | ||
86 | + (WBMarkup *) sharedMarkup { | |
87 | if (SharedMarkup_ == nil) | |
88 | SharedMarkup_ = [[WBMarkup alloc] init]; | |
89 | return SharedMarkup_; | |
90 | } | |
91 | ||
92 | - (id) init { | |
93 | if ((self = [super init]) != nil) { | |
94 | WebThreadLock(); | |
95 | ||
2de63e63 JF |
96 | view_ = [[WebView alloc] initWithFrame:CGRectMake(0, 0, 640, 5000)]; |
97 | [view_ setDrawsBackground:NO]; | |
98 | ||
99 | WebPreferences *preferences([[WebPreferences alloc] initWithIdentifier:@"com.apple.webkit.webmarkup"]); | |
100 | [preferences setPlugInsEnabled:NO]; | |
101 | [view_ setPreferences:preferences]; | |
102 | [preferences release]; | |
103 | ||
104 | window_ = [[WAKWindow alloc] initWithFrame:CGRectMake(0, 0, 640, 5000)]; | |
105 | [window_ setContentView:view_]; | |
106 | ||
107 | WebFrame *frame([view_ mainFrame]); | |
108 | [[frame frameView] setAllowsScrolling:NO]; | |
109 | [frame _setLoadsSynchronously:YES]; | |
110 | ||
111 | [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]; | |
112 | } return self; | |
113 | } | |
114 | ||
115 | - (void) dealloc { | |
116 | [window_ release]; | |
117 | [view_ release]; | |
118 | [super dealloc]; | |
119 | } | |
120 | ||
09ea4166 JF |
121 | - (void) drawRect:(CGRect)rect { |
122 | [text_ setScrollXOffset:origin_.x scrollYOffset:origin_.y]; | |
123 | ||
124 | CGRect draw(CGRectMake(0, 0, rect.size.width - rect.origin.x, rect.size.height - rect.origin.y)); | |
125 | ||
126 | CGContextSaveGState(context_); { | |
127 | CGContextTranslateCTM(context_, rect.origin.x, rect.origin.y); | |
128 | ||
129 | if (kCFCoreFoundationVersionNumber > 700) | |
130 | [view_ _drawRect:draw context:context_ lockFocus:YES]; | |
131 | else { | |
132 | WKView *view([view_ _viewRef]); | |
fdac1738 JF |
133 | WKViewLockFocus$(view); { |
134 | WKViewDisplayRect$(view, draw); | |
135 | } WKViewUnlockFocus$(view); | |
09ea4166 JF |
136 | } |
137 | } CGContextRestoreGState(context_); | |
138 | } | |
139 | ||
2de63e63 JF |
140 | - (WebView *) _webView { |
141 | return view_; | |
142 | } | |
143 | ||
144 | - (void) setStringDrawingOrigin:(CGPoint)origin { | |
145 | origin_ = origin; | |
146 | } | |
147 | ||
148 | - (void) clearStringDrawingOrigin { | |
149 | origin_ = CGPointZero; | |
150 | } | |
151 | ||
0fa37711 | 152 | - (CGSize) sizeOfMarkup:(NSString *)markup forWidth:(CGFloat)width { |
2de63e63 JF |
153 | WebThreadLock(); |
154 | ||
155 | if (![self _webPrepareContextForTextDrawing:NO]) | |
156 | return CGSizeZero; | |
157 | ||
158 | [text_ setInnerHTML:markup]; | |
159 | [text_ removeAttribute:@"style"]; | |
160 | ||
161 | NSString *value([[NSString alloc] initWithFormat:[self _styleFormatString:@"width: %.0fpx; height: 5000px"], width]); | |
162 | [size_ setAttribute:@"style" value:value]; | |
163 | [value release]; | |
164 | ||
165 | [[view_ mainFrame] forceLayoutAdjustingViewSize:YES]; | |
166 | return [[view_ mainFrame] renderedSizeOfNode:text_ constrainedToWidth:width]; | |
167 | } | |
168 | ||
0fa37711 | 169 | - (CGSize) sizeOfString:(NSString *)string withStyle:(NSString *)style forWidth:(CGFloat)width { |
2de63e63 JF |
170 | WebThreadLock(); |
171 | ||
172 | if (![self _webPrepareContextForTextDrawing:NO]) | |
173 | return CGSizeZero; | |
174 | ||
175 | [size_ removeChild:[size_ firstChild]]; | |
176 | ||
177 | WebFrame *frame([view_ mainFrame]); | |
178 | ||
179 | [frame forceLayoutAdjustingViewSize:YES]; | |
180 | [text_ setInnerText:string]; | |
181 | [self _setupWithStyle:style width:width height:5000]; | |
182 | [frame forceLayoutAdjustingViewSize:YES]; | |
183 | ||
184 | return [[view_ mainFrame] renderedSizeOfNode:text_ constrainedToWidth:width]; | |
185 | } | |
186 | ||
187 | - (NSString *) _styleFormatString:(NSString *)style { | |
188 | return style; | |
189 | } | |
190 | ||
0fa37711 | 191 | - (void) _setupWithStyle:(NSString *)style width:(CGFloat)width height:(CGFloat)height { |
2de63e63 JF |
192 | WebThreadLock(); |
193 | ||
194 | if (style != nil && [style length] != 0) | |
195 | [text_ setAttribute:@"style" value:style]; | |
196 | else | |
197 | [text_ removeAttribute:@"style"]; | |
198 | ||
199 | NSString *value([[NSString alloc] initWithFormat:[self _styleFormatString:@"width: %.0fpx; height: %.0fpx"], width, height]); | |
200 | [size_ setAttribute:@"style" value:value]; | |
201 | [value release]; | |
202 | ||
203 | [size_ appendChild:text_]; | |
204 | } | |
205 | ||
206 | - (BOOL) _webPrepareContextForTextDrawing:(BOOL)drawing { | |
207 | WebThreadLock(); | |
208 | ||
209 | if (document_ == nil) { | |
210 | WebFrame *frame([view_ mainFrame]); | |
211 | ||
212 | document_ = [[frame DOMDocument] retain]; | |
213 | if (document_ == nil) { | |
214 | NSLog(@"*** ERROR: no DOM document in text-drawing webview"); | |
215 | return NO; | |
216 | } | |
217 | ||
218 | text_ = [[document_ getElementById:@"text"] retain]; | |
219 | size_ = [[document_ getElementById:@"size"] retain]; | |
220 | ||
221 | if (text_ == nil || size_ == nil) { | |
222 | NSLog(@"*** ERROR: cannot find DOM element required for text drawing"); | |
223 | return NO; | |
224 | } | |
225 | } | |
226 | ||
227 | context_ = NULL; | |
228 | ||
229 | if (!drawing) | |
230 | context_ = NULL; | |
231 | else { | |
232 | context_ = WKGetCurrentGraphicsContext(); | |
233 | if (context_ == NULL) { | |
234 | NSLog(@"*** ERROR: no CGContext set for drawing"); | |
235 | return NO; | |
236 | } | |
237 | } | |
238 | ||
239 | return YES; | |
240 | } | |
241 | ||
242 | - (void) drawMarkup:(NSString *)markup atPoint:(CGPoint)point { | |
243 | [self drawMarkup:markup inRect:CGRectMake(point.x, point.y, 65535, 65535)]; | |
244 | } | |
245 | ||
246 | - (void) drawMarkup:(NSString *)markup inRect:(CGRect)rect { | |
247 | WebThreadLock(); | |
248 | ||
249 | if (![self _webPrepareContextForTextDrawing:YES]) | |
250 | return; | |
251 | ||
252 | [text_ setInnerHTML:markup]; | |
253 | [text_ removeAttribute:@"style"]; | |
254 | ||
255 | NSString *value([[NSString alloc] initWithFormat:[self _styleFormatString:@"width: %.0fpx; height: %.0fpx"], CGRectGetWidth(rect), CGRectGetHeight(rect)]); | |
256 | [size_ setAttribute:@"style" value:value]; | |
257 | [value release]; | |
258 | ||
259 | [[view_ mainFrame] forceLayoutAdjustingViewSize:YES]; | |
260 | ||
09ea4166 | 261 | [self drawRect:rect]; |
2de63e63 JF |
262 | } |
263 | ||
264 | - (void) drawString:(NSString *)string atPoint:(CGPoint)point withStyle:(NSString *)style { | |
265 | [self drawString:string inRect:CGRectMake(point.x, point.y, 65535, 65535) withStyle:style]; | |
266 | } | |
267 | ||
268 | - (void) drawString:(NSString *)string inRect:(CGRect)rect withStyle:(NSString *)style { | |
269 | WebThreadLock(); | |
270 | ||
271 | if (![self _webPrepareContextForTextDrawing:YES]) | |
272 | return; | |
273 | ||
274 | [size_ removeChild:[size_ firstChild]]; | |
275 | ||
276 | WebFrame *frame([view_ mainFrame]); | |
277 | ||
278 | [frame forceLayoutAdjustingViewSize:YES]; | |
279 | [text_ setInnerText:string]; | |
280 | [self _setupWithStyle:style width:CGRectGetWidth(rect) height:CGRectGetHeight(rect)]; | |
281 | [frame forceLayoutAdjustingViewSize:YES]; | |
282 | ||
09ea4166 | 283 | [self drawRect:rect]; |
2de63e63 JF |
284 | } |
285 | ||
286 | @end |