/** * Callback for defer scripts */ static nserror convert_script_defer_cb(hlcache_handle *script, const hlcache_event *event, void *pw) { html_content *parent = pw; unsigned int i; struct html_script *s; /* Find script */ for (i = 0, s = parent->scripts; i != parent->scripts_count; i++, s++) { if (s->type == HTML_SCRIPT_DEFER && s->data.handle == script) break; } assert(i != parent->scripts_count); switch (event->type) { case CONTENT_MSG_LOADING: break; case CONTENT_MSG_READY: break; case CONTENT_MSG_DONE: LOG(("script %d done '%s'", i, nsurl_access(hlcache_handle_get_url(script)))); parent->base.active--; LOG(("%d fetches active", parent->base.active)); break; case CONTENT_MSG_ERROR: LOG(("script %s failed: %s", nsurl_access(hlcache_handle_get_url(script)), event->data.error)); hlcache_handle_release(script); s->data.handle = NULL; parent->base.active--; LOG(("%d fetches active", parent->base.active)); content_add_error(&parent->base, "?", 0); break; case CONTENT_MSG_STATUS: break; default: assert(0); } /* if there are no active fetches remaining begin post parse * conversion */ if (html_can_begin_conversion(parent)) { html_begin_conversion(parent); } return NSERROR_OK; }
static nserror html_convert_css_callback(hlcache_handle *css, const hlcache_event *event, void *pw) { html_content *parent = pw; unsigned int i; struct html_stylesheet *s; /* Find sheet */ for (i = 0, s = parent->stylesheets; i != parent->stylesheet_count; i++, s++) { if (s->sheet == css) break; } assert(i != parent->stylesheet_count); switch (event->type) { case CONTENT_MSG_DONE: LOG("done stylesheet slot %d '%s'", i, nsurl_access(hlcache_handle_get_url(css))); parent->base.active--; LOG("%d fetches active", parent->base.active); break; case CONTENT_MSG_ERROR: LOG("stylesheet %s failed: %s", nsurl_access(hlcache_handle_get_url(css)), event->data.error); hlcache_handle_release(css); s->sheet = NULL; parent->base.active--; LOG("%d fetches active", parent->base.active); content_add_error(&parent->base, "?", 0); break; case CONTENT_MSG_POINTER: /* Really don't want this to continue after the switch */ return NSERROR_OK; default: break; } if (html_can_begin_conversion(parent)) { html_begin_conversion(parent); } return NSERROR_OK; }
/** * Callback for syncronous scripts */ static nserror convert_script_sync_cb(hlcache_handle *script, const hlcache_event *event, void *pw) { html_content *parent = pw; unsigned int i; struct html_script *s; script_handler_t *script_handler; dom_hubbub_error err; /* Find script */ for (i = 0, s = parent->scripts; i != parent->scripts_count; i++, s++) { if (s->type == HTML_SCRIPT_SYNC && s->data.handle == script) break; } assert(i != parent->scripts_count); switch (event->type) { case CONTENT_MSG_LOADING: break; case CONTENT_MSG_READY: break; case CONTENT_MSG_DONE: LOG(("script %d done '%s'", i, nsurl_access(hlcache_handle_get_url(script)))); parent->base.active--; LOG(("%d fetches active", parent->base.active)); s->already_started = true; /* attempt to execute script */ script_handler = select_script_handler(content_get_type(s->data.handle)); if (script_handler != NULL) { /* script has a handler */ const char *data; unsigned long size; data = content_get_source_data(s->data.handle, &size ); script_handler(parent->jscontext, data, size); } /* continue parse */ err = dom_hubbub_parser_pause(parent->parser, false); if (err != DOM_HUBBUB_OK) { LOG(("unpause returned 0x%x", err)); } break; case CONTENT_MSG_ERROR: LOG(("script %s failed: %s", nsurl_access(hlcache_handle_get_url(script)), event->data.error)); hlcache_handle_release(script); s->data.handle = NULL; parent->base.active--; LOG(("%d fetches active", parent->base.active)); content_add_error(&parent->base, "?", 0); s->already_started = true; /* continue parse */ err = dom_hubbub_parser_pause(parent->parser, false); if (err != DOM_HUBBUB_OK) { LOG(("unpause returned 0x%x", err)); } break; case CONTENT_MSG_STATUS: break; default: assert(0); } /* if there are no active fetches remaining begin post parse * conversion */ if (html_can_begin_conversion(parent)) { html_begin_conversion(parent); } return NSERROR_OK; }
static nserror html_convert_css_callback(hlcache_handle *css, const hlcache_event *event, void *pw) { html_content *parent = pw; unsigned int i; struct html_stylesheet *s; /* Find sheet */ for (i = 0, s = parent->stylesheets; i != parent->stylesheet_count; i++, s++) { if (s->sheet == css) break; } assert(i != parent->stylesheet_count); switch (event->type) { case CONTENT_MSG_LOADING: break; case CONTENT_MSG_READY: break; case CONTENT_MSG_DONE: LOG(("done stylesheet slot %d '%s'", i, nsurl_access(hlcache_handle_get_url(css)))); parent->base.active--; LOG(("%d fetches active", parent->base.active)); break; case CONTENT_MSG_ERROR: LOG(("stylesheet %s failed: %s", nsurl_access(hlcache_handle_get_url(css)), event->data.error)); hlcache_handle_release(css); s->sheet = NULL; parent->base.active--; LOG(("%d fetches active", parent->base.active)); content_add_error(&parent->base, "?", 0); break; case CONTENT_MSG_STATUS: if (event->data.explicit_status_text == NULL) { /* Object content's status text updated */ html_set_status(parent, content_get_status_message(css)); content_broadcast(&parent->base, CONTENT_MSG_STATUS, event->data); } else { /* Object content wants to set explicit message */ content_broadcast(&parent->base, CONTENT_MSG_STATUS, event->data); } break; case CONTENT_MSG_POINTER: /* Really don't want this to continue after the switch */ return NSERROR_OK; default: assert(0); } if (html_can_begin_conversion(parent)) { html_begin_conversion(parent); } return NSERROR_OK; }
nserror favicon_callback(hlcache_handle *icon, const hlcache_event *event, void *pw) { struct content *c = pw; switch (event->type) { case CONTENT_MSG_LOADING: /* check that the favicon is really a correct image type */ if (content_get_type(icon) == CONTENT_UNKNOWN) { union content_msg_data msg_data; LOG(("%s is not a favicon", content_get_url(icon))); hlcache_handle_abort(icon); hlcache_handle_release(icon); c->data.html.favicon = NULL; c->active -= 1; content_add_error(c, "NotFavIco", 0); msg_data.error = messages_get("NotFavIco"); content_broadcast(c, CONTENT_MSG_STATUS, msg_data); } break; case CONTENT_MSG_READY: break; case CONTENT_MSG_DONE: c->active -= 1; break; case CONTENT_MSG_ERROR: LOG(("favicon %s failed: %s", content_get_url(icon), event->data.error)); hlcache_handle_release(c->data.html.favicon); c->data.html.favicon = NULL; content_add_error(c, "?", 0); c->active -= 1; break; case CONTENT_MSG_STATUS: content_broadcast(c, CONTENT_MSG_STATUS, event->data); break; case CONTENT_MSG_REDRAW: /* Fall through */ case CONTENT_MSG_REFRESH: /* Fall through */ case CONTENT_MSG_REFORMAT: break; default: assert(0); } if (c->active == 0) { /* all objects have arrived */ content__reformat(c, c->available_width, c->height); html_set_status(c, ""); content_set_done(c); } return NSERROR_OK; }
static nserror html_object_callback(hlcache_handle *object, const hlcache_event *event, void *pw) { struct content_html_object *o = pw; html_content *c = (html_content *) o->parent; int x, y; struct box *box; assert(c->base.status != CONTENT_STATUS_ERROR); box = o->box; if (box == NULL && event->type != CONTENT_MSG_ERROR) { return NSERROR_OK; } switch (event->type) { case CONTENT_MSG_LOADING: if (c->base.status != CONTENT_STATUS_LOADING && c->bw != NULL) content_open(object, c->bw, &c->base, box->object_params); break; case CONTENT_MSG_READY: if (content_can_reformat(object)) { /* TODO: avoid knowledge of box internals here */ content_reformat(object, false, box->max_width != UNKNOWN_MAX_WIDTH ? box->width : 0, box->max_width != UNKNOWN_MAX_WIDTH ? box->height : 0); /* Adjust parent content for new object size */ html_object_done(box, object, o->background); if (c->base.status == CONTENT_STATUS_READY || c->base.status == CONTENT_STATUS_DONE) content__reformat(&c->base, false, c->base.available_width, c->base.height); } break; case CONTENT_MSG_DONE: c->base.active--; LOG("%d fetches active", c->base.active); html_object_done(box, object, o->background); if (c->base.status != CONTENT_STATUS_LOADING && box->flags & REPLACE_DIM) { union content_msg_data data; if (!box_visible(box)) break; box_coords(box, &x, &y); data.redraw.x = x + box->padding[LEFT]; data.redraw.y = y + box->padding[TOP]; data.redraw.width = box->width; data.redraw.height = box->height; data.redraw.full_redraw = true; content_broadcast(&c->base, CONTENT_MSG_REDRAW, data); } break; case CONTENT_MSG_ERROR: hlcache_handle_release(object); o->content = NULL; if (box != NULL) { c->base.active--; LOG("%d fetches active", c->base.active); content_add_error(&c->base, "?", 0); html_object_failed(box, c, o->background); } break; case CONTENT_MSG_REDRAW: if (c->base.status != CONTENT_STATUS_LOADING) { union content_msg_data data = event->data; if (!box_visible(box)) break; box_coords(box, &x, &y); if (object == box->background) { /* Redraw request is for background */ css_fixed hpos = 0, vpos = 0; css_unit hunit = CSS_UNIT_PX; css_unit vunit = CSS_UNIT_PX; int width = box->padding[LEFT] + box->width + box->padding[RIGHT]; int height = box->padding[TOP] + box->height + box->padding[BOTTOM]; int t, h, l, w; /* Need to know background-position */ css_computed_background_position(box->style, &hpos, &hunit, &vpos, &vunit); w = content_get_width(box->background); if (hunit == CSS_UNIT_PCT) { l = (width - w) * hpos / INTTOFIX(100); } else { l = FIXTOINT(nscss_len2px(hpos, hunit, box->style)); } h = content_get_height(box->background); if (vunit == CSS_UNIT_PCT) { t = (height - h) * vpos / INTTOFIX(100); } else { t = FIXTOINT(nscss_len2px(vpos, vunit, box->style)); } /* Redraw area depends on background-repeat */ switch (css_computed_background_repeat( box->style)) { case CSS_BACKGROUND_REPEAT_REPEAT: data.redraw.x = 0; data.redraw.y = 0; data.redraw.width = box->width; data.redraw.height = box->height; break; case CSS_BACKGROUND_REPEAT_REPEAT_X: data.redraw.x = 0; data.redraw.y += t; data.redraw.width = box->width; break; case CSS_BACKGROUND_REPEAT_REPEAT_Y: data.redraw.x += l; data.redraw.y = 0; data.redraw.height = box->height; break; case CSS_BACKGROUND_REPEAT_NO_REPEAT: data.redraw.x += l; data.redraw.y += t; break; default: break; } data.redraw.object_width = box->width; data.redraw.object_height = box->height; /* Add offset to box */ data.redraw.x += x; data.redraw.y += y; data.redraw.object_x += x; data.redraw.object_y += y; content_broadcast(&c->base, CONTENT_MSG_REDRAW, data); break; } else { /* Non-background case */ if (hlcache_handle_get_content(object) == event->data.redraw.object) { int w = content_get_width(object); int h = content_get_height(object); if (w != 0) { data.redraw.x = data.redraw.x * box->width / w; data.redraw.width = data.redraw.width * box->width / w; } if (h != 0) { data.redraw.y = data.redraw.y * box->height / h; data.redraw.height = data.redraw.height * box->height / h; } data.redraw.object_width = box->width; data.redraw.object_height = box->height; } data.redraw.x += x + box->padding[LEFT]; data.redraw.y += y + box->padding[TOP]; data.redraw.object_x += x + box->padding[LEFT]; data.redraw.object_y += y + box->padding[TOP]; } content_broadcast(&c->base, CONTENT_MSG_REDRAW, data); } break; case CONTENT_MSG_REFRESH: if (content_get_type(object) == CONTENT_HTML) { /* only for HTML objects */ guit->misc->schedule(event->data.delay * 1000, html_object_refresh, o); } break; case CONTENT_MSG_LINK: /* Don't care about favicons that aren't on top level content */ break; case CONTENT_MSG_GETCTX: *(event->data.jscontext) = NULL; break; case CONTENT_MSG_SCROLL: if (box->scroll_x != NULL) scrollbar_set(box->scroll_x, event->data.scroll.x0, false); if (box->scroll_y != NULL) scrollbar_set(box->scroll_y, event->data.scroll.y0, false); break; case CONTENT_MSG_DRAGSAVE: { union content_msg_data msg_data; if (event->data.dragsave.content == NULL) msg_data.dragsave.content = object; else msg_data.dragsave.content = event->data.dragsave.content; content_broadcast(&c->base, CONTENT_MSG_DRAGSAVE, msg_data); } break; case CONTENT_MSG_SAVELINK: case CONTENT_MSG_POINTER: case CONTENT_MSG_SELECTMENU: case CONTENT_MSG_GADGETCLICK: /* These messages are for browser window layer. * we're not interested, so pass them on. */ content_broadcast(&c->base, event->type, event->data); break; case CONTENT_MSG_CARET: { union html_focus_owner focus_owner; focus_owner.content = box; switch (event->data.caret.type) { case CONTENT_CARET_REMOVE: case CONTENT_CARET_HIDE: html_set_focus(c, HTML_FOCUS_CONTENT, focus_owner, true, 0, 0, 0, NULL); break; case CONTENT_CARET_SET_POS: html_set_focus(c, HTML_FOCUS_CONTENT, focus_owner, false, event->data.caret.pos.x, event->data.caret.pos.y, event->data.caret.pos.height, event->data.caret.pos.clip); break; } } break; case CONTENT_MSG_DRAG: { html_drag_type drag_type = HTML_DRAG_NONE; union html_drag_owner drag_owner; drag_owner.content = box; switch (event->data.drag.type) { case CONTENT_DRAG_NONE: drag_type = HTML_DRAG_NONE; drag_owner.no_owner = true; break; case CONTENT_DRAG_SCROLL: drag_type = HTML_DRAG_CONTENT_SCROLL; break; case CONTENT_DRAG_SELECTION: drag_type = HTML_DRAG_CONTENT_SELECTION; break; } html_set_drag_type(c, drag_type, drag_owner, event->data.drag.rect); } break; case CONTENT_MSG_SELECTION: { html_selection_type sel_type; union html_selection_owner sel_owner; if (event->data.selection.selection) { sel_type = HTML_SELECTION_CONTENT; sel_owner.content = box; } else { sel_type = HTML_SELECTION_NONE; sel_owner.none = true; } html_set_selection(c, sel_type, sel_owner, event->data.selection.read_only); } break; default: break; } if (c->base.status == CONTENT_STATUS_READY && c->base.active == 0 && (event->type == CONTENT_MSG_LOADING || event->type == CONTENT_MSG_DONE || event->type == CONTENT_MSG_ERROR)) { /* all objects have arrived */ content__reformat(&c->base, false, c->base.available_width, c->base.height); content_set_done(&c->base); } /* If 1) the configuration option to reflow pages while objects are * fetched is set * 2) an object is newly fetched & converted, * 3) the box's dimensions need to change due to being replaced * 4) the object's parent HTML is ready for reformat, * 5) the time since the previous reformat is more than the * configured minimum time between reformats * then reformat the page to display newly fetched objects */ else if (nsoption_bool(incremental_reflow) && event->type == CONTENT_MSG_DONE && box != NULL && !(box->flags & REPLACE_DIM) && (c->base.status == CONTENT_STATUS_READY || c->base.status == CONTENT_STATUS_DONE) && (wallclock() > c->base.reformat_time)) { content__reformat(&c->base, false, c->base.available_width, c->base.height); } return NSERROR_OK; }