From e65e41e2d6efccba983cd63aadfb10b5b4a935b2 Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Sat, 23 May 2020 20:38:41 +0100 Subject: move content handler specific selection copying into handlers --- content/content_protected.h | 11 +- content/handlers/html/html.c | 1 + content/handlers/html/textselection.c | 227 ++++++++++++++++++++++++++++++++++ content/handlers/html/textselection.h | 2 + content/handlers/text/textplain.c | 79 ++++++++---- content/handlers/text/textplain.h | 14 --- 6 files changed, 295 insertions(+), 39 deletions(-) (limited to 'content') diff --git a/content/content_protected.h b/content/content_protected.h index 9a3654beb..02bc8614d 100644 --- a/content/content_protected.h +++ b/content/content_protected.h @@ -47,6 +47,7 @@ struct browser_window_features; struct textsearch_context; struct box; struct selection; +struct selection_string; typedef struct content_handler content_handler; @@ -112,7 +113,10 @@ struct content_handler { nserror (*textsearch_bounds)(struct content *c, unsigned start_idx, unsigned end_idx, struct box *start_ptr, struct box *end_ptr, struct rect *bounds_out); /** - * cause a region of the content to be marked invalid and hence redraw + * redraw an area of selected text + * + * The defined text selection will cause an area of the + * content to be marked as invalid and hence redrawn. * * \param c The content being redrawn * \param start_idx The start index of the text region to be redrawn @@ -121,6 +125,11 @@ struct content_handler { */ nserror (*textselection_redraw)(struct content *c, unsigned start_idx, unsigned end_idx); + /** + * copy selected text into selection string possibly with formatting + */ + nserror (*textselection_copy)(struct content *c, unsigned start_idx, unsigned end_idx, struct selection_string *selstr); + /** * create a selection object */ diff --git a/content/handlers/html/html.c b/content/handlers/html/html.c index 2a3cb1774..a8ae9e452 100644 --- a/content/handlers/html/html.c +++ b/content/handlers/html/html.c @@ -2336,6 +2336,7 @@ static const content_handler html_content_handler = { .textsearch_find = html_textsearch_find, .textsearch_bounds = html_textsearch_bounds, .textselection_redraw = html_textselection_redraw, + .textselection_copy = html_textselection_copy, .create_selection = html_create_selection, .no_share = true, }; diff --git a/content/handlers/html/textselection.c b/content/handlers/html/textselection.c index bbd55732e..11e9deeb4 100644 --- a/content/handlers/html/textselection.c +++ b/content/handlers/html/textselection.c @@ -277,6 +277,7 @@ html_create_selection(struct content *c, struct selection **sel_out) return NSERROR_OK; } +/* exported interface documented in html/textselection.h */ nserror html_textselection_redraw(struct content *c, unsigned start_idx, @@ -301,3 +302,229 @@ html_textselection_redraw(struct content *c, return NSERROR_OK; } + +/** + * Selection traversal routine for appending text to a string + * + * \param text pointer to text being added, or NULL for newline + * \param length length of text to be appended (bytes) + * \param box pointer to text box, or NULL if from textplain + * \param len_ctx Length conversion context + * \param handle selection string to append to + * \param whitespace_text whitespace to place before text for formatting + * may be NULL + * \param whitespace_length length of whitespace_text + * \return true iff successful and traversal should continue + */ +static bool +selection_copy(const char *text, + size_t length, + struct box *box, + const nscss_len_ctx *len_ctx, + struct selection_string *handle, + const char *whitespace_text, + size_t whitespace_length) +{ + bool add_space = false; + plot_font_style_t style; + plot_font_style_t *pstyle = NULL; + + /* add any whitespace which precedes the text from this box */ + if (whitespace_text != NULL && + whitespace_length > 0) { + if (!selection_string_append(whitespace_text, + whitespace_length, + false, + pstyle, + handle)) { + return false; + } + } + + if (box != NULL) { + /* HTML */ + add_space = (box->space != 0); + + if (box->style != NULL) { + /* Override default font style */ + font_plot_style_from_css(len_ctx, box->style, &style); + pstyle = &style; + } else { + /* If there's no style, there must be no text */ + assert(box->text == NULL); + } + } + + /* add the text from this box */ + if (!selection_string_append(text, length, add_space, pstyle, handle)) { + return false; + } + + return true; +} + + +/** + * Traverse the given box subtree, calling selection copy for all + * boxes that lie (partially) within the given range + * + * \param box box subtree + * \param len_ctx Length conversion context. + * \param start_idx start of range within textual representation (bytes) + * \param end_idx end of range + * \param handler handler function to call + * \param handle handle to pass + * \param before type of whitespace to place before next encountered text + * \param first whether this is the first box with text + * \param do_marker whether deal enter any marker box + * \return false iff traversal abandoned part-way through + */ +static bool +traverse_tree(struct box *box, + const nscss_len_ctx *len_ctx, + unsigned start_idx, + unsigned end_idx, + struct selection_string *selstr, + save_text_whitespace *before, + bool *first, + bool do_marker) +{ + struct box *child; + const char *whitespace_text = ""; + size_t whitespace_length = 0; + + assert(box); + + /* If selection starts inside marker */ + if (box->parent && + box->parent->list_marker == box && + !do_marker) { + /* set box to main list element */ + box = box->parent; + } + + /* If box has a list marker */ + if (box->list_marker) { + /* do the marker box before continuing with the rest of the + * list element */ + if (!traverse_tree(box->list_marker, + len_ctx, + start_idx, + end_idx, + selstr, + before, + first, + true)) { + return false; + } + } + + /* we can prune this subtree, it's after the selection */ + if (box->byte_offset >= end_idx) { + return true; + } + + /* read before calling the handler in case it modifies the tree */ + child = box->children; + + /* If nicely formatted output of the selected text is required, work + * out what whitespace should be placed before the next bit of text */ + if (before) { + save_text_solve_whitespace(box, + first, + before, + &whitespace_text, + &whitespace_length); + } else { + whitespace_text = NULL; + } + + if ((box->type != BOX_BR) && + !((box->type == BOX_FLOAT_LEFT || + box->type == BOX_FLOAT_RIGHT) && + !box->text)) { + unsigned start_offset; + unsigned end_offset; + + if (selected_part(box, + start_idx, + end_idx, + &start_offset, + &end_offset)) { + if (!selection_copy(box->text + start_offset, + min(box->length, end_offset) - start_offset, + box, + len_ctx, + selstr, + whitespace_text, + whitespace_length)) { + return false; + } + if (before) { + *first = false; + *before = WHITESPACE_NONE; + } + } + } + + /* find the first child that could lie partially within the selection; + * this is important at the top-levels of the tree for pruning subtrees + * that lie entirely before the selection */ + + if (child) { + struct box *next = child->next; + + while (next && next->byte_offset < start_idx) { + child = next; + next = child->next; + } + + while (child) { + /* read before calling the handler in case it modifies + * the tree */ + struct box *next = child->next; + + if (!traverse_tree(child, + len_ctx, + start_idx, + end_idx, + selstr, + before, + first, + false)) { + return false; + } + + child = next; + } + } + + return true; +} + +/* exported interface documented in html/textselection.h */ +nserror +html_textselection_copy(struct content *c, + unsigned start_idx, + unsigned end_idx, + struct selection_string *selstr) +{ + html_content *html = (html_content *)c; + save_text_whitespace before = WHITESPACE_NONE; + bool first = true; + bool res; + + res = traverse_tree(html->layout, + &html->len_ctx, + start_idx, + end_idx, + selstr, + &before, + &first, + false); + + if (res == false) { + return NSERROR_NOMEM; + } + return NSERROR_OK; +} diff --git a/content/handlers/html/textselection.h b/content/handlers/html/textselection.h index 0287f3c58..4a866b0b2 100644 --- a/content/handlers/html/textselection.h +++ b/content/handlers/html/textselection.h @@ -34,4 +34,6 @@ nserror html_create_selection(struct content *c, struct selection **sel_out); nserror html_textselection_redraw(struct content *c, unsigned start_idx, unsigned end_idx); +nserror html_textselection_copy(struct content *c, unsigned start_idx, unsigned end_idx, struct selection_string *selstr); + #endif diff --git a/content/handlers/text/textplain.c b/content/handlers/text/textplain.c index d8f61be89..275efda83 100644 --- a/content/handlers/text/textplain.c +++ b/content/handlers/text/textplain.c @@ -1513,6 +1513,43 @@ textplain_coords_from_range(struct content *c, } +/** + * Return a pointer to the raw UTF-8 data, as opposed to the reformatted + * text to fit the window width. Thus only hard newlines are preserved + * in the saved/copied text of a selection. + * + * \param[in] c content of type CONTENT_TEXTPLAIN + * \param[in] start starting byte offset within UTF-8 text + * \param[in] end ending byte offset + * \param[out] plen receives validated length + * \return pointer to text, or NULL if no text + */ +static char * +textplain_get_raw_data(struct content *c, + unsigned start, + unsigned end, + size_t *plen) +{ + textplain_content *text = (textplain_content *) c; + size_t utf8_size; + + assert(c != NULL); + + utf8_size = text->utf8_data_size; + + /* any text at all? */ + if (!utf8_size) return NULL; + + /* clamp to valid offset range */ + if (start >= utf8_size) start = utf8_size; + if (end >= utf8_size) end = utf8_size; + + *plen = end - start; + + return text->utf8_data + start; +} + + /** * get bounds of a free text search match */ @@ -1570,6 +1607,23 @@ textplain_textselection_redraw(struct content *c, return NSERROR_OK; } +static nserror +textplain_textselection_copy(struct content *c, + unsigned start_idx, + unsigned end_idx, + struct selection_string *selstr) +{ + const char *text; + size_t length; + bool res; + + text = textplain_get_raw_data(c, start_idx, end_idx, &length); + res = selection_string_append(text, length, false, NULL, selstr); + if (res == false) { + return NSERROR_NOMEM; + } + return NSERROR_OK; +} /** * plain text content handler table @@ -1593,6 +1647,7 @@ static const content_handler textplain_content_handler = { .textsearch_find = textplain_textsearch_find, .textsearch_bounds = textplain_textsearch_bounds, .textselection_redraw = textplain_textselection_redraw, + .textselection_copy = textplain_textselection_copy, .create_selection = textplain_create_selection, .no_share = true, }; @@ -1643,28 +1698,4 @@ size_t textplain_size(struct content *c) -/* exported interface documented in html/textplain.h */ -char * -textplain_get_raw_data(struct content *c, - unsigned start, - unsigned end, - size_t *plen) -{ - textplain_content *text = (textplain_content *) c; - size_t utf8_size; - assert(c != NULL); - - utf8_size = text->utf8_data_size; - - /* any text at all? */ - if (!utf8_size) return NULL; - - /* clamp to valid offset range */ - if (start >= utf8_size) start = utf8_size; - if (end >= utf8_size) end = utf8_size; - - *plen = end - start; - - return text->utf8_data + start; -} diff --git a/content/handlers/text/textplain.h b/content/handlers/text/textplain.h index 206c5831e..b94ee33d3 100644 --- a/content/handlers/text/textplain.h +++ b/content/handlers/text/textplain.h @@ -46,18 +46,4 @@ nserror textplain_init(void); size_t textplain_size(struct content *c); -/** - * Return a pointer to the raw UTF-8 data, as opposed to the reformatted - * text to fit the window width. Thus only hard newlines are preserved - * in the saved/copied text of a selection. - * - * \param[in] c content of type CONTENT_TEXTPLAIN - * \param[in] start starting byte offset within UTF-8 text - * \param[in] end ending byte offset - * \param[out] plen receives validated length - * \return pointer to text, or NULL if no text - */ -char *textplain_get_raw_data(struct content *c, unsigned start, unsigned end, size_t *plen); - - #endif -- cgit v1.2.3