1 /* HTML frames parser */
14 #include "cache/cache.h"
15 #include "document/document.h"
16 #include "document/html/frames.h"
17 #include "document/options.h"
18 #include "document/renderer.h"
19 #include "document/view.h"
20 #include "protocol/uri.h"
21 #include "session/session.h"
22 #include "terminal/draw.h"
23 #include "util/color.h"
24 #include "util/string.h"
25 #include "util/time.h"
29 add_frameset_entry(struct frameset_desc
*frameset_desc
,
30 struct frameset_desc
*subframe
,
31 unsigned char *name
, unsigned char *url
)
33 struct frame_desc
*frame_desc
;
36 assert(frameset_desc
);
37 if_assert_failed
return;
39 /* FIXME: The following is triggered by
40 * http://www.thegoodlookingorganisation.co.uk/main.htm.
41 * There may exist a true fix for this... --Zas */
42 /* May the one truly fixing this notify'n'close bug 237 in the
43 * Bugzilla... --pasky */
44 if (frameset_desc
->box
.y
>= frameset_desc
->box
.height
)
47 offset
= frameset_desc
->box
.x
48 + frameset_desc
->box
.y
* frameset_desc
->box
.width
;
49 frame_desc
= &frameset_desc
->frame_desc
[offset
];
50 frame_desc
->subframe
= subframe
;
51 frame_desc
->name
= null_or_stracpy(name
);
52 frame_desc
->uri
= (url
&& *url
) ? get_uri(url
, 0) : NULL
;
54 frame_desc
->uri
= get_uri("about:blank", 0);
56 frameset_desc
->box
.x
++;
57 if (frameset_desc
->box
.x
>= frameset_desc
->box
.width
) {
58 frameset_desc
->box
.x
= 0;
59 frameset_desc
->box
.y
++;
60 /* FIXME: check y here ? --Zas */
64 struct frameset_desc
*
65 create_frameset(struct frameset_param
*fp
)
67 struct frameset_desc
*fd
;
71 if_assert_failed
return NULL
;
73 assertm(fp
->x
> 0 && fp
->y
> 0,
74 "Bad size of frameset: x=%d y=%d", fp
->x
, fp
->y
);
76 if (fp
->x
<= 0) fp
->x
= 1;
77 if (fp
->y
<= 0) fp
->y
= 1;
81 /* size - 1 since one struct frame_desc is already reserved
82 * in struct frameset_desc. */
83 fd
= mem_calloc(1, sizeof(*fd
)
84 + (size
- 1) * sizeof(fd
->frame_desc
[0]));
90 for (i
= 0; i
< size
; i
++) {
91 fd
->frame_desc
[i
].width
= fp
->width
[i
% fp
->x
];
92 fd
->frame_desc
[i
].height
= fp
->height
[i
/ fp
->x
];
97 fd
->box
.width
= fp
->x
;
98 fd
->box
.height
= fp
->y
;
101 add_frameset_entry(fp
->parent
, fd
, NULL
, NULL
);
107 add_frame_to_list(struct session
*ses
, struct document_view
*doc_view
)
109 struct document_view
*ses_doc_view
;
111 assert(ses
&& doc_view
);
112 if_assert_failed
return;
114 foreach (ses_doc_view
, ses
->scrn_frames
) {
115 int sx
= ses_doc_view
->box
.x
;
116 int sy
= ses_doc_view
->box
.y
;
117 int x
= doc_view
->box
.x
;
118 int y
= doc_view
->box
.y
;
120 if (sy
> y
|| (sy
== y
&& sx
> x
)) {
121 add_at_pos(ses_doc_view
->prev
, doc_view
);
126 add_to_list_end(ses
->scrn_frames
, doc_view
);
129 static struct document_view
*
130 find_fd(struct session
*ses
, unsigned char *name
,
131 int depth
, int x
, int y
)
133 struct document_view
*doc_view
;
136 if_assert_failed
return NULL
;
138 foreachback (doc_view
, ses
->scrn_frames
) {
139 if (doc_view
->used
) continue;
140 if (c_strcasecmp(doc_view
->name
, name
)) continue;
143 doc_view
->depth
= depth
;
147 doc_view
= mem_calloc(1, sizeof(*doc_view
));
148 if (!doc_view
) return NULL
;
151 doc_view
->name
= stracpy(name
);
152 if (!doc_view
->name
) {
156 doc_view
->depth
= depth
;
157 doc_view
->session
= ses
;
158 doc_view
->search_word
= &ses
->search_word
;
159 set_box(&doc_view
->box
, x
, y
, 0, 0);
161 add_frame_to_list(ses
, doc_view
);
166 static struct document_view
*
167 format_frame(struct session
*ses
, struct frame_desc
*frame_desc
,
168 struct document_options
*o
, int depth
)
170 struct view_state
*vs
;
171 struct document_view
*doc_view
;
174 assert(ses
&& frame_desc
&& o
);
175 if_assert_failed
return NULL
;
178 struct cache_entry
*cached
;
179 struct frame
*frame
= ses_find_frame(ses
, frame_desc
->name
);
181 if (!frame
) return NULL
;
184 cached
= find_in_cache(vs
->uri
);
185 if (!cached
) return NULL
;
188 if (vs
->plain
!= -1) o
->plain
= vs
->plain
;
190 if (!cached
->redirect
|| frame
->redirect_cnt
>= MAX_REDIRECTS
)
193 frame
->redirect_cnt
++;
195 vs
->uri
= get_uri_reference(cached
->redirect
);
196 #ifdef CONFIG_ECMASCRIPT
197 vs
->ecmascript_fragile
= 1;
202 doc_view
= find_fd(ses
, frame_desc
->name
, depth
, o
->box
.x
, o
->box
.y
);
204 render_document(vs
, doc_view
, o
);
205 assert(doc_view
->document
);
206 doc_view
->document
->frame
= frame_desc
;
214 format_frames(struct session
*ses
, struct frameset_desc
*fsd
,
215 struct document_options
*op
, int depth
)
217 struct document_options o
;
220 assert(ses
&& fsd
&& op
);
221 if_assert_failed
return;
223 if (depth
> HTML_MAX_FRAME_DEPTH
) {
229 o
.margin
= !!o
.margin
;
232 for (j
= 0; j
< fsd
->box
.height
; j
++) {
236 for (i
= 0; i
< fsd
->box
.width
; i
++) {
237 struct frame_desc
*frame_desc
= &fsd
->frame_desc
[n
];
239 o
.box
.width
= frame_desc
->width
;
240 o
.box
.height
= frame_desc
->height
;
241 o
.framename
= frame_desc
->name
;
242 if (frame_desc
->subframe
) {
243 format_frames(ses
, frame_desc
->subframe
, &o
, depth
+ 1);
244 } else if (frame_desc
->name
) {
245 struct document_view
*doc_view
;
247 doc_view
= format_frame(ses
, frame_desc
, &o
, depth
);
249 do_not_optimize_here(&doc_view
);
251 if (doc_view
&& document_has_frames(doc_view
->document
)) {
252 struct document
*document
= doc_view
->document
;
254 /* Make sure the frameset does not
255 * disappear during processing further
256 * frames - this can happen when we
257 * kick the same-frame-ids bug and
258 * replace view_state of an existing
259 * frame - if that frame is *this* one,
260 * it gets detached, losing the
261 * document and if we aren't lucky the
262 * document gets burned in
263 * shrink_format_cache(). Also note
264 * that the same bug replacing
265 * view_state also causes doc_view
266 * recyclation so we end up with a
267 * *different* document attached to it
268 * after the call, so we need to cache
269 * it. It's all fun to debug, really.
271 object_lock(document
);
272 format_frames(ses
, document
->frame_desc
,
274 object_unlock(document
);
277 o
.box
.x
+= o
.box
.width
+ 1;
280 /* This makes this ugly loop actually at least remotely
281 * debuggable by gdb, otherwise the compiler happily
282 * randomly trashes or just blasts away *all* of these
284 do_not_optimize_here(&n
);
285 do_not_optimize_here(&i
);
286 do_not_optimize_here(&j
);
287 do_not_optimize_here(&fsd
);
288 do_not_optimize_here(&frame_desc
);
291 o
.box
.y
+= o
.box
.height
+ 1;
296 /* Frame width computation tools */
298 /* Returns 0 on error. */
300 distribute_rows_or_cols(int *val_
, int max_value
, int *values
, int values_count
)
305 int val
= *val_
- max_value
;
307 for (i
= 0; i
< values_count
; i
++) {
311 divisor
+= values
[i
];
317 for (i
= 0; i
< values_count
; i
++) {
320 /* SIGH! gcc 2.7.2.* has an optimizer bug! */
321 do_not_optimize_here_gcc_2_7(&divisor
);
322 tmp
= values
[i
] * (divisor
- tmp_val
) / divisor
;
323 val
-= values
[i
] - tmp
;
330 for (i
= 0; i
< values_count
; i
++) {
331 if (val
< 0) values
[i
]++, val
++, flag
= 1;
332 if (val
> 0 && values
[i
] > 1) values
[i
]--, val
--, flag
= 1;
342 /* Returns 0 on error. */
344 distribute_rows_or_cols_that_left(int *val_
, int max_value
, int *values
, int values_count
)
352 tmp_values
= fmem_alloc(values_count
* sizeof(*tmp_values
));
353 if (!tmp_values
) return 0;
354 memcpy(tmp_values
, values
, values_count
* sizeof(*tmp_values
));
356 for (i
= 0; i
< values_count
; i
++)
359 val
= max_value
- val
;
361 for (i
= 0; i
< values_count
; i
++)
362 if (tmp_values
[i
] < 0)
363 divisor
+= -tmp_values
[i
];
367 for (i
= 0; i
< values_count
; i
++)
368 if (tmp_values
[i
] < 0) {
369 int tmp
= (-tmp_values
[i
] * tmp_val
/ divisor
);
374 assertm(val
>= 0, "distribute_rows_or_cols_that_left: val < 0");
375 if_assert_failed val
= 0;
377 for (i
= 0; i
< values_count
; i
++)
378 if (tmp_values
[i
] < 0 && val
)
381 assertm(val
<= 0, "distribute_rows_or_cols_that_left: val > 0");
382 if_assert_failed val
= 0;
384 fmem_free(tmp_values
);
390 /* Returns 0 on error. */
392 extract_rows_or_cols_values(unsigned char *str
, int max_value
, int pixels_per_char
,
393 int **new_values
, int *new_values_count
)
395 unsigned char *tmp_str
;
397 int values_count
= 0;
400 unsigned char *end
= str
;
402 unsigned long number
;
403 int val
= -1; /* Wildcard */
407 /* Some platforms (FreeBSD) set errno when the first char is
408 * not a digit others (GNU/Linux) don't so ignore errno. */
409 /* Extract number. */
410 number
= strtoul(str
, (char **) &end
, 10);
417 /* @number is an ulong, but @val is int,
418 * so check if @number is in a reasonable
419 * range to prevent bad things. */
420 if (number
<= 0xffff) {
421 if (*str
== '%') /* Percentage */
422 val
= int_min((int) number
, 100) * max_value
/ 100;
423 else if (*str
!= '*') /* Pixels */
424 val
= (number
+ (pixels_per_char
- 1) / 2) / pixels_per_char
;
425 else if (number
) /* Fraction, marked by negative value. */
430 tmp_values
= mem_realloc(values
, (values_count
+ 1) * sizeof(*tmp_values
));
431 if (!tmp_values
) return 0;
434 values
[values_count
++] = val
;
436 /* Check for next field if any. */
437 tmp_str
= strchr(str
, ',');
438 if (!tmp_str
) break; /* It was the last field. */
443 *new_values
= values
;
444 *new_values_count
= values_count
;
449 /* Parse rows and cols attribute values and calculate appropriated values for display.
450 * It handles things like:
451 * <frameset cols="140,260,160"> values in pixels
452 * <frameset cols="1*,2*,3*"> values in fractions
453 * <frameset cols="320,*"> wildcard
454 * <frameset cols="33%,33%,33%" rows="33%,33%,33%"> values in percentage
457 parse_frame_widths(unsigned char *str
, int max_value
, int pixels_per_char
,
458 int **new_values
, int *new_values_count
)
465 *new_values_count
= 0;
467 ret
= extract_rows_or_cols_values(str
, max_value
, pixels_per_char
,
468 &values
, &values_count
);
471 /* Here begins distribution between rows or cols. */
473 val
= 2 * values_count
- 1;
474 for (i
= 0; i
< values_count
; i
++)
476 val
+= values
[i
] - 1;
478 if (val
>= max_value
) {
479 ret
= distribute_rows_or_cols(&val
, max_value
, values
, values_count
);
483 for (i
= 0; i
< values_count
; i
++)
488 ret
= distribute_rows_or_cols_that_left(&val
, max_value
, values
, values_count
);
490 ret
= distribute_rows_or_cols(&val
, max_value
, values
, values_count
);
495 for (i
= 0; i
< values_count
; i
++)
501 for (j
= 0; j
< values_count
; j
++)
502 if (values
[j
] > maxval
)
503 maxval
= values
[j
], maxpos
= j
;
505 values
[i
] = 1, values
[maxpos
]--;
508 *new_values
= values
;
509 *new_values_count
= values_count
;