NetSurf on ARMv7 platforms
by John-Mark Bell
Afternoon,
So, NetSurf runs mostly ok on ARMv7 platforms with their backwards
incompatible unaligned LDR behaviour turned on. Tinct, however, appears
to perform some LDRs from unaligned addresses, resulting in crashes.
Here's a backtrace:
> Fatal signal received: Segmentation fault
>
> Stack backtrace:
>
> Running thread 0x59e53c
> ( 5a7ee0) pc: 462860 lr: 74f4c sp: 5a7ee4 __write_backtrace()
> ( 5a7f10) pc: 74d64 lr: 463298 sp: 5a7f14 ^ro_gui_signal()
> ( 5a7f38) pc: 463288 lr: 462f58 sp: 5a7f3c __unixlib_exec_sig()
> ( 5a7fa0) pc: 462a18 lr: 463870 sp: 5a7fa4 __unixlib_raise_signal()
> ( 5a7fb0) pc: 463774 lr: 5a6c48 sp: 5a6c04 __h_cback()
>
> Register dump at 005a7fb4:
>
> a1: 12f a2: 2d a3: 1f4 a4: 0
> v1: 0 v2: 0 v3: 202513b4 v4: 1
> v5: 3d5 v6: 0 sl: 41b78d39 fp: 67197124
> ip: 20248394 sp: 5a6c04 lr: 5a6c48 pc: 2024609c
> cpsr: 80000113
>
> 20246088 : .p.â : e21a7003 : ANDS R7,R10,#3
> 2024608c : .... : 0a000008 : BEQ &202460B4
> 20246090 : .àgâ : e267e003 : RSB R14,R7,#3
> 20246094 : ..^á : e15e0000 : CMP R14,R0
> 20246098 : .à Á : c1a0e000 : MOVGT R14,R0
> 2024609c : .??å : e59a9000 : LDR R9,[R10,#0]
> 202460a0 : ..@Ð : d040000e : SUBLE R0,R0,R14
> 202460a4 : ..?Ò : d2800003 : ADDLE R0,R0,#3
> 202460a8 : ..?À : c0800007 : ADDGT R0,R0,R7
>
> Invalid pc address bebeec
For reference, Tinct is loaded at 202416B4.
J.
13 years, 3 months
Progress report
by Paweł Blokus
Since the last progress report I have completed the hotlist and added
the sslcert window. I also removed all known bugs as well as
unintuitive tree behaviour. There are two remaining things to be done
before merging - making the branch work for riscos and reviewing the
code :). I am not sure what the preferred order here is.
The next things I would like to work at are the select and scroll
widgets. I decided to work on them in one branch because they are
pretty much tied together - the select widget needs to be scrolled and
the scroll has to be tested somehow.
14 years, 1 month
Re: Review: Mark Benjamin -- gtkmain branch
by Mark
John-Mark Bell wrote:
Hi,
first of all let me say thanks for taking the time to review the diff;
judging from the amount of time it has taken me to implement all the
little changes, that must have been not inconsiderable
>> Index: render/favicon.c
>> ===================================================================
>> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
>> +++ render/favicon.c 2009-07-10 12:49:13.000000000 +0100
>> +unsigned long favicon_hash(char *str)
>> +{
>> + if (str == NULL)
>> + return 0;
>> + unsigned long hash = 5381;
>> + int c;
>> + while ((c = (unsigned char) *str++))
>> + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
>> + return hash;
>> +}
>>
> Do we really need yet another hash function?
>
as I seem to recall the discussion at the time, there were people
encouraging me to hash the strings here; perhaps a better question would
be 'is it time to make one global hash function to replace the static
hash functions dotted around?' - aside from that, I suppose a show of
hands as to whether there are really that many people who think hashing
here is worthwhile after all? I may have to teach myself profiling as
James suggested
>> +/**
>> + * retrieve 1 url reference to 1 favicon
>> + * \param html xml node of html element
>> + * \return pointer to url; NULL for no icon
>> + */
>>
> Is the result allocated? Does the caller have to free it?
>
the documentation for url_join() is unclear :-)
more seriously, though, is there ever a case that a call to a function
returning char * doesn't need the caller to free() it when non-NULL?
Occasionally the function specifically says it accepts a param of that
buffer that must have space allocation already, so that it is clear that
the caller has to allocate as well as free; however I have added 'caller
owns returned pointer' is that right?
>> +char *favicon_get_icon_ref(struct content *c, xmlNode *html)
>> +{
>> + xmlNode *node;
>> + char *rel, *type, *href, *url, *suf, *url2;
>> + url2 = NULL;
>> + url_func_result res;
>> + int score, hiscore;
>> + hiscore = 0;
>> + /* hashed values - saves calculating them afresh every time */
>> + #define HHICON 0x7c98572e
>> + /* icon */
>> + #define HHSHORTCUTICON 0xfcbccdca
>> + /* shortcut icon */
>> + #define HHAPPLETOUCHICON 0x024c6ddd
>> + /* apple-touch-icon */
>> + #define HHIMAGEPNG 0x7382417c
>> + /* image/png */
>> + #define HHIMAGEGIF 0x73821a8d
>> + /* image/gif */
>> + #define HHIMAGEVNDMICROSOFTICON 0xdae02bba
>> + /* image.vnd.microsoft.icon */
>> + #define HHIMAGEJPEG 0xe3c72f5d
>> + /* image/jpeg */
>> + #define HHIMAGEJPG 0x73822838
>> + /* image/jpg */
>> + #define HHIMAGEICO 0x73822252
>> + /* image/ico */
>> + #define HHIMAGEICON 0xe3c66d00
>> + /* image/icon */
>> + #define HHIMAGEXICON 0x0e3e78e5
>> + /* image/x-icon */
>> + #define HHTEXTICO 0x17e966a2
>> + /* text/icon */
>> + #define HHAPPLICATIONICO 0x087b6fb4
>> + /*application/icon*/
>> + #define HHSUFICO 0x0b887ec0
>> + /* ico */
>> + #define HHSUFPNG 0x0b889dea
>> + /* png */
>> + #define HHSUFGIF 0x0b8876fb
>> + /* gif */
>> + #define HHSUFJPG 0x0b88848
>> +char *favicon_get_icon_ref(struct content *c, xmlNode *html)
>> +{
>> + xmlNode *node;
>> + char *rel, *type, *href, *url, *suf, *url2;
>> + url2 = NULL;
>> + url_func_result res;
>> + int score, hiscore;
>> + hiscore = 0;
>> + /* hashed values - saves calculating them afresh every time */
>> + #define HHICON 0x7c98572e
>> + /* icon */
>> + #define HHSHORTCUTICON 0xfcbccdca
>> + /* shortcut icon */
>> + #define HHAPPLETOUCHICON 0x024c6ddd
>> + /* apple-touch-icon */
>> + #define HHIMAGEPNG 0x7382417c
>> + /* image/png */
>> + #define HHIMAGEGIF 0x73821a8d
>> + /* image/gif */
>> + #define HHIMAGEVNDMICROSOFTICON 0xdae02bba
>> + /* image.vnd.microsoft.icon */
>> + #define HHIMAGEJPEG 0xe3c72f5d
>> + /* image/jpeg */
>> + #define HHIMAGEJPG 0x73822838
>> + /* image/jpg */
>> + #define HHIMAGEICO 0x73822252
>> + /* image/ico */
>> + #define HHIMAGEICON 0xe3c66d00
>> + /* image/icon */
>> + #define HHIMAGEXICON 0x0e3e78e5
>> + /* image/x-icon */
>> + #define HHTEXTICO 0x17e966a2
>> + /* text/icon */
>> + #define HHAPPLICATIONICO 0x087b6fb4
>> + /*application/icon*/
>> + #define HHSUFICO 0x0b887ec0
>> + /* ico */
>> + #define HHSUFPNG 0x0b889dea
>> + /* png */
>> + #define HHSUFGIF 0x0b8876fb
>> + /* gif */
>> + #define HHSUFJPG 0x0b888486
>> + /* jpg */
>> + #define HHSUFJPEG 0x7c99198b
>> + /* jpeg */
>>
> This is unpleasant. What's wrong with a simple LUT?
>
well as James suggested an LUT I've implemented an LUT + hashes for now;
as I say there were people suggesting hashes at the time;
> Actually, having read the rest of this function, I think it's hugely
> over-complicated and somewhat dubious in its examination of file
> extensions.
>
> I suggest replacing it with something far simpler that simply considers
> the value of @rel. I see no point whatsoever in considering either the
> value of @type or any filename extension -- these are more likely than
> not to be wrong and are thus worthless as an indicator of suitability.
>
what do we do then when a page says 'link rel="myfavrel"
type="application/ico" href="favicon.ico"'? The trouble with favicons is
that it is one area where standards took time catching up with practice,
so that standards are breached more often than observed; then there is
the case of multiple link rels, usually all 3 kinds, you then need to
try to pick the best as [aside from apple-touch] the judgement of the
page writer may be entirely arbitrary, such is the very large variance
in respect for standards
>> + if (url2 == NULL) {
>> + struct url_components comp;
>> + if (url_get_components(c->data.html.base_url, &comp) !=
>> + URL_FUNC_OK)
>> + return NULL;
>> + if (url_normalize(comp.authority,
>> + &url) != URL_FUNC_OK)
>> + return NULL;
>> + url_destroy_components(&comp);
>> + if (url_join("/favicon.ico", url, &url2) != URL_FUNC_OK)
>> + return NULL;
>>
> This is overcomplex and broken, too. You simply want:
>
> url_join("/favicon.ico", c->data.html.base_url, &result);
>
I had the impression that for an url such as
http://www.mysite.com/path/to/file/file.html then base_url would be
http://www.mysite.com/path/to/file ? I need
http://www.mysite.com/favicon.ico
>> +/**
>> + * retrieve 1 favicon
>> + * \param c content structure
>> + * \param html xml node of html element
>> + * \return true for success, false for error
>> + */
>> +
>> +bool favicon_get_icon(struct content *c, xmlNode *html)
>> +{
>> + union content_msg_data msg_data;
>> + char *url = favicon_get_icon_ref(c, html);
>> + struct content *favcontent = NULL;
>> + if (url != NULL)
>> + favcontent = fetchcache(url, favicon_callback,
>> + (intptr_t) c, 0, c->width, c->height, true, 0,
>> + 0, false, false);
>> + free(url);
>> + if (favcontent == NULL)
>> + return false;
>>
> The above would be clearer as:
>
> if (url == NULL)
> return false;
>
> favcontent = fetchcache();
>
> free(url);
>
> if (favcontent == NULL)
> return false;
>
understood
>> +
>> + c->data.html.favicon = favcontent;
>> +
>> + fetchcache_go(favcontent, c->url, favicon_callback,
>> + (intptr_t) c, 0, c->width, c->height,
>> + 0, 0, false, c->url);
>> +
>> + if (!c->data.html.favicon) {
>> + msg_data.error = "Favicon failed to load";
>>
> Should there be a call to content_broadcast here?
>
Your call; although I understand the fetch system a bit better now, I
seem to recall clearing the call to content_broadcast from the place I
borrowed the logic, as it was inappropriate here; subsequent to James'
suggestions back in May
>> Index: render/favicon.h
>> ===================================================================
>> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
>> +++ render/favicon.h 2009-07-10 12:49:13.000000000 +0100
>> +typedef enum {
>> + HHICON = 0x7c98572e,
>>
> [...]
>
>> +} favicon_string_hash;
>>
> Why is this here? What needs to know about it?
>
It was moved to favicon.c to replace the #defines in r8717-8718
>> +
>> +bool favicon_get_icon(struct content *c, xmlNode *html);
>>
> You need to #include <libxml/tree.h> and forward declare struct content
> in this file.
>
understood
>> Index: framebuffer/fb_search.c
>> ===================================================================
>> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
>> +++ framebuffer/fb_search.c 2009-07-10 12:49:34.000000000 +0100
>> +/* put new search_web globals here for now */
>> +char *search_engines_file_location;
>> +char *search_default_ico_location;
>>
> These should be core options, just like the cookie file/jar path.
>
done; need to double-check beos/framebuffer/amiga compile
>> +/**
>> + * display hourglass while searching
>> + * \param active start/stop indicator
>> + */
>> +void gui_search_set_hourglass(bool active)
>> +{
>> +}
>>
> I'm somewhat confused as to the need for this. Nothing else in the core
> needs a way of saying "I'm busy", so why is search special?
>
really I tried to maintain the integrity of the search implementation in
riscos/search.c during its migration to the core; hence rather than
disrupting functions, I migrate the functions as they are to the core as
much as possible; as the function call was present in the riscos
implementation, I suppose as it may take a while for riscos to look for
matches, the function call has moved to the core; besides, rather than
displaying a pointer hourglass, a front may want to display a toolbar
hourglass etc; moreover wasn't there a suggestion to make a core widget
for search bar? Then perhaps tinkering with the actual search functions
could wait
>> +/**
>> + * retrieve string being searched for from gui
>> + */
>> +char *gui_search_get_string(void)
>>
> Who owns the result. Does the caller have to free it?
> What happens if there isn't a search string?
>
comment amended
>> +{
>> +}
>>
> This is missing a return value.
>
amended
>> +/**
>> + * add search string to recent searches list
>> + * \param string search pattern
>> + */
>> +void gui_search_add_recent(const char *string)
>>
> Presumably, this gets copied? If so, what happens on memory exhaustion?
>
A presumably else it wouldn't be const, no? B wouldn't that be up to the
implementation? For instance as it stands for now, I seem to think
riscos implements a list of searches, no-one else
>> +/**
>> + * activate search forwards button in gui
>> + * \param active activate/inactivate
>> + */
>> +void gui_search_set_forward_state(bool inactive)
>>
> The documentation doesn't match the API. Please make it "bool active",
> as it's less prone to misinterpretation.
>
blame riscos :-) amended
>> +/**
>> + * activate search forwards button in gui
>> + * \param active activate/inactivate
>> + */
>> +void gui_search_set_back_state(bool inactive)
>>
> Ditto.
>
>> +/**
>> + * retrieve state of 'case sensitive' check in gui
>> + */
>> +bool gui_search_get_case_sens(void)
>>
> _case_sensitive, please.
>
>> +{
>> +}
>>
> Return statement missing.
>
>> +/**
>> + * retrieve state of 'show all' check in gui
>> + */
>> +bool gui_search_get_show_all(void)
>> +{
>> +}
>>
> Ditto.
>
all sorted
>> Index: gtk/gtk_save.c
>> ===================================================================
>> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
>> +++ gtk/gtk_save.c 2009-07-10 12:49:36.000000000 +0100
>> +bool save_complete_gui_save(const char *path, const char *filename, struct content *c, int len, char *sourcedata, int type)
>>
> Needs wrapping to 80 columns. Also, needs doxygen comments.
>
doxygen comments are in desktop/save_complete.h, is that not correct?
>> +{
>> + int res;
>> + int namelen;
>> + namelen = strlen(path) + strlen(filename) + 2;
>>
> These can be merged into a single statement. What is the "+ 2" for?
>
'/', '\0'; comment added
>> + char *fullpath = malloc(namelen);
>> + if (!fullpath) {
>>
> if (fullpath == NULL)
>> + warn_user("NoMemory", 0);
>> + return false;
>> + }
>> + snprintf(fullpath, namelen, "%s/%s", path, filename);
>>
>
> You know the buffer is big enough already, so could reasonably use
> memcpy (assuming you retain the length of path).
>
save that I need to add '/', '\0' so snprintf does all of that nicely;
hence its apparent popularity among NetSurf devs :-)
>> + FILE *f = fopen(fullpath, "w"); /* may need mode 'b' when c != NULL */
>>
> Always open it as binary, then.
>
are you sure? Filesystem work being mildly tricky, release 2.1 is fresh
in the mind :-D
>> +int save_complete_htmlSaveFileFormat(const char *path, const char *filename,
>> + xmlDocPtr cur, const char *encoding, int format)
>>
> Needs doxygen.
>
see desktop/save_complete.h, I hope that doxygen itself makes the
necessary reference
>> +{
>> + int ret;
>> + int len = strlen(path) + strlen(filename) + 2;
>> + char *finame = malloc(len);
>>
> Can this be named better; it's not clear what it's meant to be.
>
fullpathfilename? There is me trying to save space, you say NetSurf
should be compact, then I'm hearing sens->sensitive, ( != NULL ) etc
>> + if (!finame){
>> + warn_user("NoMemory", 0);
>> + return -1;
>> + }
>> + snprintf(finame, len, "%s/%s", path, filename);
>>
> Again, you know the buffer's big enough.
>
again snprintf does it better :-D
>> +bool save_complete_gui_filetype(const char *path, const char *filename,
>> + int type)
>>
> Documentation. What's this for, anyway? You pass what I assume is type
> information to save_complete_gui_save().
>
save_complete.h; as I recall, riscos sets the filetype *after* saving
for the base html document, so needs an additional call to set the filetype
>> Index: gtk/gtk_theme.c
>> ===================================================================
>> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
>> +++ gtk/gtk_theme.c 2009-07-10 12:49:36.000000000 +0100
>> +char *current_theme_name = NULL;
>>
> I'd prefer to see this declared static. Provide accessors if you must.
>
done
>> +void nsgtk_theme_init()
>>
> Needs void. Also, documentation.
>
>> +{
>> + if (option_current_theme == 0)
>> + return;
>> + char *themefile = g_strconcat(res_dir_location, "themelist", NULL);
>>
> No check for failure.
>
as g_strconcat only ever breaks the program in low memory; amended to
malloc/snprintf
>> + nsgtk_scaffolding *list = scaf_list;
>> + nsgtk_theme_verify(NULL);
>>
> I can't work out what special behaviour this triggers. Lack of comments
> isn't helping.
>
>> + FILE *fp = fopen(themefile, "r");
>> + char buf[50];
>> + int row_count = 0;
>> + if (fp == NULL)
>> + return;
>>
> I'd prefer to see checks for calls failing immediately after the call
> where possible.
>
I try to put the declarations together; even though we're freeer in gtk
than in the core, it seems good practice; however, amended :-)
>> + while (fgets(buf, sizeof(buf), fp)) {
>>
> != NULL
>
>> +void nsgtk_theme_add(const char *themename)
>>
> Documentation.
>
Mmm :-)
>> +{
>> + GtkWidget *notification, *label;
>> + char *labelcontent, *themefile = g_strconcat(res_dir_location,
>> + "themelist", NULL);
>>
> Check for failure?
>
>> + /* conduct verification here; no adding duplicates to list */
>> + if (nsgtk_theme_verify(themename) == false) {
>> + warn_user(messages_get("gtkThemeDup"), 0);
>> + g_free(themefile);
>> + return;
>> + }
>> + FILE *fp = fopen(themefile, "a");
>> + if (fp == NULL) {
>> + warn_user(messages_get("gtkFileError"), themefile);
>> + g_free(themefile);
>> + return;
>> + }
>> + fprintf(fp, "%s\n", themename);
>> + fclose(fp);
>> + g_free(themefile);
>> +
>> + /* notification that theme was added successfully */
>> + notification = gtk_dialog_new_with_buttons(messages_get("gtkThemeAdd"),
>> + NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK,
>> + GTK_RESPONSE_NONE, NULL);
>>
>
> Ditto.
>
>
>> + labelcontent = g_strconcat("\t\t\t", messages_get("gtkThemeAdd"),
>> + "\t\t\t", NULL);
>>
>
> Ditto.
>
>
>> + label = gtk_label_new(labelcontent);
>>
>
> Ditto.
>
>
>> + g_signal_connect_swapped(notification, "response",
>> + G_CALLBACK(gtk_widget_destroy), notification);
>> + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(notification)->vbox), label);
>> + gtk_widget_show_all(notification);
>> + g_free(labelcontent);
>> +
>> + /* update combo */
>> + if (wndPreferences == NULL)
>> + return;
>> + nsgtk_options_combo_theme_add(themename);
>>
> I can't work out if themename is being leaked or not.
>
as it's being passed as const, I hope not :-) some alterations to
container_* made accordingly :-D
>> +}
>> +
>> +bool nsgtk_theme_verify(const char *themename)
>>
> Documentation.
>
so it would seem :-D
>> +{
>> + long filelength;
>> + FILE *fp;
>> + size_t val;
>> + char buf[50];
>> + char *themefile = g_strconcat(res_dir_location, "themelist", NULL);
>>
> Check for failure
>> + if (themename == NULL) {
>> + char *filecontent, *testfile;
>> + struct stat sta;
>> + fp = fopen(themefile, "r+");
>> + if (fp == NULL) {
>> + warn_user(messages_get("gtkFileError"), themefile);
>> + g_free(themefile);
>> + return true;
>> + }
>> + fseek(fp, 0L, SEEK_END);
>> + filelength = ftell(fp);
>> + filecontent = malloc(filelength + 2);
>>
> Check for failure
>> + strcpy(filecontent, "gtk default theme\n");
>> + if (filecontent == NULL) {
>>
> It's a bit late now
>> + warn_user(messages_get("NoMemory"), 0);
>> + g_free(themefile);
>> + return true;
>> + }
>> + fseek(fp, 0L, SEEK_SET);
>> + while (fgets(buf, sizeof(buf), fp)) {
>>
> != NULL
>> + /* iterate list */
>> + buf[strlen(buf) - 1] = '\0';
>> + testfile = g_strconcat(res_dir_location, "themes/",
>> + buf, NULL);
>>
> Check for failure
>> + /* check every directory */
>> + if (access(testfile, R_OK) == 0) {
>> + stat(testfile, &sta);
>> + if (S_ISDIR(sta.st_mode)) {
>> + free(testfile);
>> + buf[strlen(buf)] = '\n';
>>
> You've just replaced the trailing NUL with \n.
>
there are at this point [pre-replacement] 2 trailing NULs; I had had to
clear the trailing \n to compare names
>> + strcat(filecontent, buf);
>>
> And then used strcat(), which expects a NUL-terminated string.
>
It gets precisely what it asks for :-)
>> + }
>> + }
>> + }
>>
> The above will overrun the filecontent block. The length of "gtk default
> theme\n" is not factored into the allocation request.
>
the length of "gtk default theme\n" in every normal case is already in
filelength modulo deliberate corruption of the themelist file that is of
course possible; however as it does no harm I've added more space then;
aside from that the length of filecontent must be <= the length of the
file as every entry in filecontent is read from the file
>> + fclose(fp);
>> + fp = fopen(themefile, "w");
>> + if (fp == NULL) {
>> + warn_user(messages_get("gtkFileError"), themefile);
>> + free(filecontent);
>> + g_free(themefile);
>> + return true;
>> + }
>> + val = fwrite(filecontent, strlen(filecontent), 1, fp);
>> + if (val == 0)
>> + LOG(("empty write themelist"));
>> + fclose(fp);
>> + free(filecontent);
>> + g_free(themefile);
>> + return true;
>> + } else {
>> + fp = fopen(themefile, "r");
>> + if (fp == NULL) {
>> + warn_user(messages_get("gtkFileError"), themefile);
>> + g_free(themefile);
>> + return false;
>> + }
>> + while (fgets(buf, sizeof(buf), fp)) {
>>
> != NULL
>> + buf[strlen(buf) - 1] = '\0';
>> + if (strcmp(buf, themename) == 0) {
>> + g_free(themefile);
>> + return false;
>>
> You don't close the file here.
>
>> + }
>> + }
>> + fclose(fp);
>> + g_free(themefile);
>> + return true;
>> + }
>> +
>> +}
>> +
>> +void nsgtk_theme_implement(struct gtk_scaffolding *g)
>>
> Documentation.
>
>> +{
>> + struct nsgtk_theme *theme[4];
>> + int i;
>> + for (i = 0; i < 3; i++)
>>
> 3? What is this?
>
as a result of a need to maintain integrity of object references it is
necessary to load 3 sets of gtk images then set 1 set for the main menu
images, 1 set for the right click menu [visible when the main menu is
hidden], 1 set for the popup menu [visible 'at all times' subject to
hiding items; it *may* be possible to manage that with g_object_ref() /
unref() though honestly I would have hoped to save more delicate
'optimisation' improvements until the main branch is stably merged
>> + theme[i] = nsgtk_theme_load(GTK_ICON_SIZE_MENU);
>>
>> + theme[3] = nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR);
>>
>
> Ditto.
>
> Can you use an enum here, instead?
>
>
>> + for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
>> + if ((i == URL_BAR_ITEM) || (i == THROBBER_ITEM) ||
>> + (i == WEBSEARCH_ITEM))
>> + continue;
>> + if (nsgtk_scaffolding_button(g, i)->main != NULL) {
>>
> Is nsgtk_scaffolding_button guaranteed to return a valid pointer
currently it is although it's a delicate task of making sure the calls
are properly ordered; additional condition added
>> +struct nsgtk_theme *nsgtk_theme_load(GtkIconSize s)
>>
> Documentation
>> +{
>> + if (current_theme_name == NULL)
>> + return nsgtk_theme_default(s);
>> + struct nsgtk_theme *theme = malloc(sizeof(struct nsgtk_theme));
>>
> Check for failure?
>
>> + if ((theme_cache_menu == NULL) || (theme_cache_toolbar == NULL))
>>
> Are these global?
>
static globals for cache
>> + nsgtk_theme_prepare();
>>
> What if this fails? Can it fail?
>
It shouldn't as there is a test for current_theme_name == NULL that
should call nsgtk_theme_default(); however I've added a condition for
low memory
>> +void nsgtk_theme_prepare(void)
>>
> Documentation.
>
>> +{
>> + if (current_theme_name == NULL)
>> + return;
>> + if (theme_cache_menu == NULL)
>> + theme_cache_menu = malloc(sizeof(struct nsgtk_theme_cache));
>>
> Check for failure
>> + if (theme_cache_toolbar == NULL)
>> + theme_cache_toolbar = malloc(sizeof(struct nsgtk_theme_cache));
>>
> Ditto.
>
>> + char *path = g_strconcat(res_dir_location, "themes/",
>> + current_theme_name, "/", NULL);
>>
> Ditto.
>
>> + char *filename;
>> +#define CACHE_IMAGE(p, q)\
>> + filename = g_strconcat(path, #q, ".png", NULL);\
>>
> Ditto.
>
>> + theme_cache_toolbar->image[p##_BUTTON] =\
>> + gdk_pixbuf_new_from_file_at_size(filename, 24, 24,\
>> + NULL);\
>> + theme_cache_menu->image[p##_BUTTON] =\
>> + gdk_pixbuf_new_from_file_at_size(filename, 16, 16,\
>> + NULL);\
>> + g_free(filename)
>>
> Need a blank line here. It's not clear where the macro body ends,
> otherwise.
>
>> + CACHE_IMAGE(BACK, back);
>>
> [...]
>
>> +#define CACHE_IMAGE(p, q)\
>> + filename = g_strconcat(path, #q, ".png", NULL);\
>>
> Check for failure?
>
>> + theme_cache_toolbar->searchimage[p] =\
>> + gdk_pixbuf_new_from_file_at_size(filename, 24, 24,\
>> + NULL);\
>> + theme_cache_menu->searchimage[p] =\
>> + gdk_pixbuf_new_from_file_at_size(filename, 16, 16,\
>> + NULL);\
>> + g_free(filename)
>>
> Blank line.
>
>> +GtkImage *nsgtk_theme_image_default(int i, GtkIconSize s)
>>
> Documentation
>> +{
>> + char *imagefile;
>> + GtkImage *image;
>> + switch(i) {
>> +#define BUTTON_IMAGE(p, q)\
>> + case p##_BUTTON:\
>> i+ return GTK_IMAGE(gtk_image_new_from_stock(#q, s))
>>
> Blank line
>> + BUTTON_IMAGE(BACK, gtk-go-back);
>> + case HISTORY_BUTTON:
>> + imagefile = g_strconcat(res_dir_location,
>> + "arrow_down_8x32.png", NULL);
>>
> Error handling
>> + image = GTK_IMAGE(gtk_image_new_from_file(imagefile));
>> + g_free(imagefile);
>> + return image;
>>
>> + default:
>> + imagefile = g_strconcat(res_dir_location, "themes/Alpha.png",
>> + NULL);
>>
> Error handling
>> + image = GTK_IMAGE(gtk_image_new_from_file(imagefile));
>> + g_free(imagefile);
>> + return image;
>> + }
>> +}
>> +
>> +
>> +#ifdef WITH_THEME_INSTALL
>> +/**
>> + * Handle CONTENT_THEME
>> + */
>> +void theme_install_start(struct content *c)
>>
> Documentation -- what's there doesn't match the function.
>
>> +{
>> + assert(c);
>> + assert(c->type == CONTENT_THEME);
>> +
>> + /* stop theme sitting in memory cache */
>> + c->fresh = false;
>> + if (!content_add_user(c, theme_install_callback, 0, 0)) {
>>
>
> Use == false, instead.
>
>
>> +/**
>> + * Callback for fetchcache() for theme install fetches.
>> + */
>> +void theme_install_callback(content_msg msg, struct content *c,
>> + intptr_t p1, intptr_t p2, union content_msg_data data)
>> +{
>> + switch (msg) {
>> + case CONTENT_MSG_DONE:
>> + theme_install_content = c;
>> + if (!theme_install_read(c->source_data, c->source_size))
>>
> == false.
>
>> +/**
>> + * handler saves theme data as a local theme
>> + */
>> +bool theme_install_read(char *data, unsigned long len)
>>
> Document parameters. Why is data not const?
>
>> +{
>> + char *filename;
>> + int handle = g_file_open_tmp("nsgtkthemeXXXXXX", &filename, NULL);
>>
> Failure?
>
>> +struct nsgtk_theme *nsgtk_theme_default(GtkIconSize s)
>>
> Documentation.
>
>> +{
>> + struct nsgtk_theme *theme = malloc(sizeof(struct nsgtk_theme));
>>
> Failure?
>
>> + for (int i = BACK_BUTTON; i <= PLACEHOLDER_BUTTON + 2; i++)
>>
> + 2 seems magical.
>
>> Index: gtk/gtk_toolbar.c
>> ===================================================================
>> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
>> +++ gtk/gtk_toolbar.c 2009-07-10 12:49:36.000000000 +0100
>> +static gboolean nsgtk_toolbar_store_return(GtkWidget *widget, GdkDragContext *gdc, gint x, gint y, guint time, gpointer data);
>>
> 80 columns.
>
>> +/**
>> + * change behaviour of scaffoldings while editing toolbar
>>
> This isn't very informative. What does it change? Why?
>
>> + */
>> +void nsgtk_toolbar_customization_init(struct gtk_scaffolding *g)
>> +{
>> + int i;
>> + nsgtk_scaffolding *list = scaf_list;;
>>
> Spurious semicolon.
>
I rather think that's stylish; kind of thinking of c++ :-D
>> +/**
>> + * create store window
>> + */
>> +void nsgtk_toolbar_window_open(struct gtk_scaffolding *g)
>> +{
>> + int x,y;
>> + struct nsgtk_theme *theme =
>> + nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR);
>>
> Failure?
>
is not a possibility :-)
however, in case of massive system-wide memory depletion, I'll add a
check :-D
>> + window->glade = glade_xml_new(glade_toolbar_file_location,
>> + "toolbarwindow", NULL);
>>
> Failure?
>
>> +#define GET_TOOLWIDGET(p, q) window->p = glade_xml_get_widget(window->glade,\
>> + #q)
>>
> Failure? Also, blank line.
>
>> + window->numberh = 6;
>>
> This field isn't very obvious.
>
>> + for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
>> + if (i == URL_BAR_ITEM)
>> + continue;
>> + window->store_buttons[i] =
>> + nsgtk_toolbar_make_widget(g, i, theme);
>>
> Failure?
>
>> +/**
>> + * set toolbar logical -> physical
>>
> What does this mean?
>
that the physically visible toolbar buttons are made to correspond to
the logically stored toolbar buttons in terms of location, visibility, etc
>> + */
>> +void nsgtk_toolbar_set_physical(struct gtk_scaffolding *g)
>> +{
>> + int i;
>> + struct nsgtk_theme *theme =
>> + nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR);
>>
> Failure
>> +/**
>> + * physical update of all toolbars; resensitize
>> + * \param g the 'front' scaffolding that called customize
>> + */
>>
> This doesn't appear to match what I'd expect from the function name.
>
Haha well the alternative is a thorn in your eye :-)
more detail added to the comment
>> +void nsgtk_toolbar_close(struct gtk_scaffolding *g)
>> +{
>> + int i;
>> + nsgtk_scaffolding *list = scaf_list;
>> + while (list) {
>> + struct nsgtk_theme *theme =
>> + nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR);
>>
> Failure
>> +/**
>> + * physically add widgets to store window
>> + */
>> +bool nsgtk_toolbar_add_store_widget(GtkWidget *widget)
>> +{
>> + if (window->numberh >= 6) {
>>
> What is so magical about 6? Use a #define or similar
right you are
>> + window->currentbar = gtk_toolbar_new();
>>
> Failure?
>
>> + gtk_widget_set_size_request(widget, 111, 70);
>>
> These numbers look magical.
>
they are kind of, aren't they brilliant? :-)
they seem to fit the 'normal' width of a label so that sufficient of it
is recognisable, without setting all the widths to the width of the
widest etc; height is 'normal' wrt the height of the images
>> +/**
>> + * called when a widget is dropped onto the toolbar
>> + */
>> +gboolean nsgtk_toolbar_data(GtkWidget *widget, GdkDragContext *gdc, gint x,
>> + gint y, guint time, gpointer data)
>> +{
>> + struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
>> + int ind = gtk_toolbar_get_drop_index(nsgtk_scaffolding_toolbar(g),
>> + x, y);
>> + int q, i;
>> + if (window->currentbutton == -1)
>> + return TRUE;
>> + struct nsgtk_theme *theme =
>> + nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR);
>>
> Failure
>> +gboolean nsgtk_toolbar_move_complete(GtkWidget *widget, GdkDragContext *gdc,
>> + gint x, gint y, GtkSelectionData *selection, guint info, guint
>> + time, gpointer data)
>>
> Documentation. Spurious tab.
>
>> +/**
>> + * called when hovering an item above the toolbar
>> + */
>> +gboolean nsgtk_toolbar_action(GtkWidget *widget, GdkDragContext *gdc, gint x,
>> + gint y, guint time, gpointer data)
>>
> Spurious tab.
>
>> +/**
>> + * widget factory for creation of toolbar item widgets
>> + * \param g the reference scaffolding
>> + * \param i the id of the widget
>> + * \param theme the theme to make the widgets from
>> + */
>> +GtkWidget *nsgtk_toolbar_make_widget(struct gtk_scaffolding *g, int i,
>> + struct nsgtk_theme *theme)
>> +{
>> + GtkWidget *w = NULL, *image = NULL, *hbox = NULL, *entry = NULL;
>> + char *label;
>> + GtkStockItem item;
>> + switch(i){
>> +#define MAKE_STOCKBUTTON(p, q) case p##_BUTTON:\
>> + gtk_stock_lookup(#q, &item);\
>> + label = remove_underscores(item.label, false);\
>>
> Can this fail?
>
it could return NULL as I've now changed it; so a test before free()ing
is in order
>> + w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET(\
>> + theme->image[p##_BUTTON]),label));\
>>
> Failure?
>
NULL
>> + free(label);\
>> + break
>>
> Blank line.
>
>> + case URL_BAR_ITEM:
>> + label = g_strconcat(res_dir_location, "netsurf-16x16.xpm", NULL);
>>
> Failure?
>
>> + hbox = gtk_hbox_new(FALSE, 0);
>>
> Ditto.
>
>> + image = GTK_WIDGET(gtk_image_new_from_file(label));
>>
> Ditto.
>
>> + g_free(label);
>> + entry = GTK_WIDGET(gtk_entry_new());
>>
> Ditto.
>
>> + gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
>> + gtk_box_pack_end(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
>> + w = GTK_WIDGET(gtk_tool_item_new());
>>
> Ditto.
>
>> + gtk_container_add(GTK_CONTAINER(w), hbox);
>> + gtk_tool_item_set_expand(GTK_TOOL_ITEM(w), TRUE);
>> + break;
>> + case THROBBER_ITEM:
>> + if (edit_mode)
>> + return GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET(
>> + gtk_image_new_from_pixbuf(
>> + nsgtk_throbber->framedata[0])),
>> + "[throbber]"));
>>
> Ditto.
>
>> + image = GTK_WIDGET(gtk_image_new_from_pixbuf(
>> + nsgtk_throbber->framedata[0]));
>>
> Ditto.
>
>> + w = GTK_WIDGET(gtk_tool_item_new());
>>
> Ditto.
>
>> + gtk_container_add(GTK_CONTAINER(w), image);
>> + break;
>> + case WEBSEARCH_ITEM:
>> + if (edit_mode)
>> + return GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET(
>> + gtk_image_new_from_stock("gtk-find",
>> + GTK_ICON_SIZE_LARGE_TOOLBAR)),
>> + "[websearch]"));
>>
> Ditto.
>
>> + hbox = gtk_hbox_new(FALSE, 0);
>>
> Ditto.
>
>> + image = GTK_WIDGET(gtk_image_new_from_stock("gtk-info",
>> + GTK_ICON_SIZE_LARGE_TOOLBAR));
>>
> Ditto.
>
>> + entry = GTK_WIDGET(gtk_entry_new());
>>
> Ditto.
>
>> + gtk_widget_set_size_request(entry, 77, -1);
>> + gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
>> + gtk_box_pack_end(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
>> + w = GTK_WIDGET(gtk_tool_item_new());
>>
> Ditto.
>
>> + gtk_container_add(GTK_CONTAINER(w), hbox);
>> + break;
>> +#define MAKE_MENUBUTTON(p, q) case p##_BUTTON:\
>> + label = remove_underscores(messages_get(#q), false);\
>>
> Can this fail?
>
>> + w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET(\
>> + theme->image[p##_BUTTON]), label));\
>>
> Failure?
>
>> + free(label);\
>> + break
>>
> Blank line.
>
>> +/**
>> + * \return toolbar item id when a widget is an element of the scaffolding
>> + * else -1
>> + */
>> +int nsgtk_toolbar_get_id_from_widget(GtkWidget *widget, struct gtk_scaffolding
>> + *g)
>>
> Ugh. Split on commas, not in the middle of a list item.
>
>> +void nsgtk_toolbar_connect_all(struct gtk_scaffolding *g)
>>
> Documentation.
>
>> +/**
>> + * load toolbar settings from file
>> + */
>> +void nsgtk_toolbar_customization_load(struct gtk_scaffolding *g)
>> +{
>> + int i, ii;
>> + char *val;
>> + char buffer[SLEN("11;|") * 2 * PLACEHOLDER_BUTTON]; /* numbers 0-99 */
>> + buffer[0] = '\0';
>> + char *buffer1, *subbuffer, *ptr, *pter;
>> + FILE *f = fopen(toolbar_indices_file_location, "r");
>>
> Failure?
>
>> Index: gtk/gtk_menu.c
>> ===================================================================
>> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
>> +++ gtk/gtk_menu.c 2009-07-10 12:49:36.000000000 +0100
>> +static unsigned int key;
>> +static GdkModifierType mod;
>>
> What are these for?
>
to accept parsed key/modifier values
>> +#define IMAGE_ITEM(p, q, r)\
>> + ret->q##_menuitem = GTK_IMAGE_MENU_ITEM(\
>>
> Where has ret come from?
>
it's defined in the individual functions
>> + gtk_image_menu_item_new_with_mnemonic(\
>> + messages_get(#r)));\
>>
> Failure?
>
>> + gtk_accelerator_parse(messages_get(#r "Accel"), &key, &mod);\
>> + if (key > 0)\
>> + gtk_widget_add_accelerator(GTK_WIDGET(ret->q##_menuitem),\
>> + "activate", group, key, mod, GTK_ACCEL_VISIBLE);\
>> + gtk_menu_shell_append(GTK_MENU_SHELL(ret->p##_menu),\
>> + GTK_WIDGET(ret->q##_menuitem));\
>> + gtk_widget_show(GTK_WIDGET(ret->q##_menuitem))
>>
> Blank line.
>
>> +#define CHECK_ITEM(p, q, r)\
>> + ret->q##_menuitem = GTK_CHECK_MENU_ITEM(\
>> + gtk_check_menu_item_new_with_mnemonic(\
>> + messages_get(#r)));\
>>
> Failure?
>
>> + gtk_menu_shell_append(GTK_MENU_SHELL(ret->p##_menu),\
>> + GTK_WIDGET(ret->q##_menuitem));\
>> + gtk_widget_show(GTK_WIDGET(ret->q##_menuitem))
>> +
>> +#define SET_SUBMENU(q)\
>> + ret->q##_submenu = nsgtk_menu_##q##_submenu(group);\
>> + gtk_menu_item_set_submenu(GTK_MENU_ITEM(ret->q##_menuitem),\
>> + GTK_WIDGET(ret->q##_submenu->q##_menu))
>>
> Blank line.
>
>> +#define ADD_SEP(q)\
>> + w = gtk_separator_menu_item_new();\
>>
> Where has 'w' come from?
>> + gtk_menu_shell_append(GTK_MENU_SHELL(ret->q##_menu), w);\
>> + gtk_widget_show(w)
>>
> Blank line.
>
>> +struct nsgtk_file_menu *nsgtk_menu_file_menu(GtkAccelGroup *group)
>>
> Documentation!
>
>> +{
>> + GtkWidget *w;
>> + struct nsgtk_file_menu *ret = malloc(sizeof(struct nsgtk_file_menu));
>>
> Failure?
>
>> + ret->file_menu = GTK_MENU(gtk_menu_new());
>>
> Failure?
>
>> +struct nsgtk_edit_menu *nsgtk_menu_edit_menu(GtkAccelGroup *group)
>>
> Documentation.
>
>> +{
>> + GtkWidget *w;
>> + struct nsgtk_edit_menu *ret = malloc(sizeof(struct nsgtk_edit_menu));
>>
> Failure?
>
>> + ret->edit_menu = GTK_MENU(gtk_menu_new());
>>
> Failure?
>
>> +struct nsgtk_view_menu *nsgtk_menu_view_menu(GtkAccelGroup *group)
>>
> Documentation?
>
>> +{
>> + GtkWidget *w;
>> + struct nsgtk_view_menu *ret = malloc(sizeof(struct nsgtk_view_menu));
>>
> Failure?
>
>> + ret->view_menu = GTK_MENU(gtk_menu_new());
>>
> Ditto. This is getting tiresome.
>
Hence the macros :-)
generally I have now added checks for no memory, as well as NULL returns
from gtk calls; some parameters to gtk calls may be sent as NULL in the
remaining cases without harm as far as I know; documentation added all
round; you could have simply said that there are many dittos in menu.c :-D
>> +struct nsgtk_nav_menu *nsgtk_menu_nav_menu(GtkAccelGroup *group)
>>
> Documentation.
>
>> +{
>> + GtkWidget *w;
>> + struct nsgtk_nav_menu *ret = malloc(sizeof(struct nsgtk_nav_menu));
>>
> Failure?
>
>> + ret->nav_menu = GTK_MENU(gtk_menu_new());
>>
> Failure?
>
>> +struct nsgtk_tabs_menu *nsgtk_menu_tabs_menu(GtkAccelGroup *group)
>>
> Documentation.
>
>> +{
>> + struct nsgtk_tabs_menu *ret = malloc(sizeof(struct nsgtk_tabs_menu));
>>
> Failure?
>
>> + ret->tabs_menu = GTK_MENU(gtk_menu_new());
>>
> Ditto.
>
>> +struct nsgtk_help_menu *nsgtk_menu_help_menu(GtkAccelGroup *group)
>>
> Documentation.
>
>> +{
>> + GtkWidget *w;
>> + struct nsgtk_help_menu *ret = malloc(sizeof(struct nsgtk_help_menu));
>>
> Failure?
>
>> + ret->help_menu = GTK_MENU(gtk_menu_new());
>>
> Failure?
>
>> +struct nsgtk_export_submenu *nsgtk_menu_export_submenu(GtkAccelGroup *group)
>>
> Documentation.
>
>> +{
>> + struct nsgtk_export_submenu *ret = malloc(sizeof(struct
>> + nsgtk_export_submenu));
>>
> Failure?
>
>> + ret->export_menu = GTK_MENU(gtk_menu_new());
>>
> Failure?
>
>> +struct nsgtk_scaleview_submenu *nsgtk_menu_scaleview_submenu(GtkAccelGroup
>> + *group)
>>
> Documentation. Hideous linebreak style.
>
really? well you're the boss, I'd have said it's nicer that way
>> +{
>> + struct nsgtk_scaleview_submenu *ret = malloc(sizeof(struct
>> + nsgtk_scaleview_submenu));
>>
> Failure? Again, nasty linebreak.
>
>> + ret->scaleview_menu = GTK_MENU(gtk_menu_new());
>>
> Failure?
>
>> +struct nsgtk_images_submenu *nsgtk_menu_images_submenu(GtkAccelGroup *group)
>>
> Documentation?
>
>> +{
>> + struct nsgtk_images_submenu *ret = malloc(sizeof(struct
>> + nsgtk_images_submenu));
>>
> Failure? Line breaking.
>
>> + ret->images_menu = GTK_MENU(gtk_menu_new());
>>
> Failure?
>
>> +struct nsgtk_toolbars_submenu *nsgtk_menu_toolbars_submenu(GtkAccelGroup *group)
>>
> Documentation.
>
>> +{
>> + struct nsgtk_toolbars_submenu *ret = malloc(sizeof(struct
>> + nsgtk_toolbars_submenu));
>>
> You know what I'm going to write here:
> Failure? Line breaking.
>
You do that :-)
>> + ret->toolbars_menu = GTK_MENU(gtk_menu_new());
>>
> Failure?
>
>> +struct nsgtk_debugging_submenu *nsgtk_menu_debugging_submenu(GtkAccelGroup
>> + *group)
>>
> Documentation. Line breaking.
>
>> +{
>> + struct nsgtk_debugging_submenu *ret = malloc(sizeof(struct
>> + nsgtk_debugging_submenu));
>>
> Failure? Line breaking.
>
>> + ret->debugging_menu = GTK_MENU(gtk_menu_new());
>>
> Failure?
>
>> Index: gtk/gtk_search.c
>> ===================================================================
>> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
>> +++ gtk/gtk_search.c 2009-07-10 12:49:36.000000000 +0100
>> + /** \file
>> + * Free text search (implementation)
>>
> Is it? I'd have thought it's the frontend component.
>
>> +void nsgtk_search_init(struct gtk_scaffolding *g);
>>
> Is this meant to be static?
>
I suppose not as the button needs connecting to it
>> +gboolean nsgtk_search_forward_button_clicked(GtkWidget *widget, gpointer data)
>>
> Documentation.
>
>> +gboolean nsgtk_search_back_button_clicked(GtkWidget *widget, gpointer data)
>>
> Ditto.
>
>> +void nsgtk_search_init(struct gtk_scaffolding *g)
>>
> Yes, then. Documentation.
>
No really :-D
it's accessible to the gtk area for the reasons I say
>> +gboolean nsgtk_search_close_button_clicked(GtkWidget *widget, gpointer data)
>>
> Documentation.
>
>> +gboolean nsgtk_search_entry_changed(GtkWidget *widget, gpointer data)
>>
> Ditto.
>
>> +gboolean nsgtk_search_entry_activate(GtkWidget *widget, gpointer data)
>>
> Ditto.
>
>> +gboolean nsgtk_search_entry_key(GtkWidget *widget, GdkEventKey *event,
>> + gpointer data)
>>
> Ditto.
>
>> +gboolean nsgtk_websearch_activate(GtkWidget *widget, gpointer data)
>>
> Ditto.
>
>> +gboolean nsgtk_websearch_clear(GtkWidget *widget, GdkEventFocus *f,
>> + gpointer data)
>>
> Ditto.
>
>> +void gui_search_set_status(bool found)
>>
> Ditto.
>
>> +void gui_search_set_hourglass(bool active)
>>
> Ditto. C.f. framebuffer comments.
>
documentation in the desktop header file
>> +char *gui_search_get_string(void)
>>
> Ditto.
>
>> +void gui_search_add_recent(const char *string)
>>
> Ditto.
>
>> +{
>> + recent_search[0] = strdup(string);
>>
> Failure?
>
well as it's a very bare implementation for now, NULL is acceptable
>> +void gui_search_set_forward_state(bool inactive)
>> +void gui_search_set_back_state(bool inactive)
>> +bool gui_search_get_case_sens(void)
>> +bool gui_search_get_show_all(void)
>>
> Documentation.
>
>> Index: gtk/gtk_theme.h
>> ===================================================================
>> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
>> +++ gtk/gtk_theme.h 2009-07-10 12:49:37.000000000 +0100
>> +extern char *current_theme_name;
>>
> C.f. gtk_theme.c
>
>> +struct nsgtk_theme {
>> + GtkImage *image[PLACEHOLDER_BUTTON];
>> + GtkImage *searchimage[3]; /* back, forward, close */
>> + /* apng throbber element */
>> +};
>>
> Haven't I seen this exact struct declared elsewhere? Does it need to be
> public?
>
No, perhaps you're thinking of nsgtk_theme_cache that caches a different
kind of image?
the reason it is public is that toolbar.c needs access as well as theme.c
>> Index: beos/beos_search.cpp
>> ===================================================================
>> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
>> +++ beos/beos_search.cpp 2009-07-10 12:50:00.000000000 +0100
>>
> c.f. framebuffer.
>
>> Index: riscos/searchweb.c
>> ===================================================================
>> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
>> +++ riscos/searchweb.c 2009-07-10 12:50:10.000000000 +0100
>> +char *search_engines_file_location;
>> +char *search_default_ico_location;
>>
> These should be core options.
>
>> +struct content *search_ico;
>>
> What is this?
>
cleared
>> Index: desktop/searchweb.c
>> ===================================================================
>> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
>> +++ desktop/searchweb.c 2009-07-10 12:50:16.000000000 +0100
>> + /** \file
>> + * Free text search (core)
>>
> I don't think that's accurate.
>
what do you want me to say?
>> +static struct search_provider {
>> + char *name;
>> + char *hostname;
>> + char *searchstring;
>> + char *ico;
>> +} current_search_provider;
>>
> Documentation.
>
>> +struct content *search_ico = 0;
>>
> NULL. Why does riscos/searchweb.c also define a global named search_ico?
>
>> +bool search_web_new_window(struct browser_window *bw, const char *searchterm)
>>
> Documentation!
>
>> +{
>> + char *encsearchterm;
>> + char *url;
>> + if (url_escape(searchterm,0, true, NULL, &encsearchterm) !=
>> + URL_FUNC_OK)
>> + return false;
>> + url = search_web_get_url(encsearchterm);
>>
> Failure?
>
> Where is encsearchterm freed?
>
>> +bool search_is_url(const char *url)
>>
> Documentation.
>
>> +/**
>> + * caches the details of the current web search provider
>> + * \param reference the enum value of the provider
>> + * browser init code [as well as changing preferences code] should call
>> + * search_web_provider_details(option_search_provider)
>> + */
>> +
>> +void search_web_provider_details(int reference)
>>
> If reference is an enum value, then why is it an int here?
>
as it's simpler?
>> + while (fgets(buf, sizeof(buf), f)) {
>>
> != NULL.
>
>> + strcpy(delim, "|");
>>
> Is this not just a constant?
>
>> + if (current_search_provider.name)
>>
> != NULL.
>
>> + free(current_search_provider.name);
>> + current_search_provider.name = strdup(strtok(buf, delim));
>>
> Failure?
>
>> + if (current_search_provider.hostname)
>> + free(current_search_provider.hostname);
>> + current_search_provider.hostname = strdup(strtok(NULL, delim));
>>
> Ditto.
>
>> + if (current_search_provider.searchstring)
>> + free(current_search_provider.searchstring);
>> + current_search_provider.searchstring = strdup(strtok(NULL, delim));
>>
> Ditto.
>
>> + if (current_search_provider.ico)
>> + free(current_search_provider.ico);
>> + current_search_provider.ico = strdup(strtok(NULL, delim));
>>
> Ditto.
>
>> +char *search_web_from_term(const char *searchterm)
>>
> Documentation.
>
>> +{
>> + char *encsearchterm;
>> + if (url_escape(searchterm, 0, true, NULL, &encsearchterm)
>> + != URL_FUNC_OK)
>> + return strdup(searchterm);
>> + return search_web_get_url(encsearchterm);
>> +}
>>
> This leaks encsearchterm.
>
quite right; I remember at the time thinking whether url_escape needed
free()ing afterwards, in hindsight it's very
clear :-)
>> +
>> +char *search_web_provider_name()
>> +char *search_web_provider_host()
>> +char *search_web_ico_name()
>>
> Documentation, void.
>
>> +char *search_web_get_url(const char *encsearchterm)
>>
> Documentation.
>
>> +{
>> + char *pref, *ret;
>> + int len;
>> + if (current_search_provider.searchstring)
>> + pref = strdup(current_search_provider.searchstring);
>> + else
>> + pref = strdup("http://www.google.com/search?q=%s ");
>>
> Failure?
>
>> + len = strlen(encsearchterm) + strlen(pref);
>> + ret = malloc(len -1);
>>
> Failure? *Minus* 1?
>
quite so; + '\0' - "%s"
>> +struct content *search_web_retrieve_ico(bool localdefault)
>>
> Documentation.
>
>> +{
>> + char *url;
>> + if (localdefault) {
>> + if (search_default_ico_location == NULL)
>> + return NULL;
>> + url = malloc(SLEN("file://") + strlen(
>> + search_default_ico_location) + 1);
>>
> Failure?
>
>> + strcpy(url, "file://");
>> + strcat(url, search_default_ico_location);
>> + } else {
>> + url = search_web_ico_name();
>> + }
>> +
>> + struct content *icocontent = NULL;
>> + if (url != NULL)
>> + icocontent = fetchcache(url, search_web_ico_callback,
>> + 0, 0, 20, 20, true, 0,
>> + 0, false, false);
>>
> Failure?
>
>> + free(url);
>> + if (icocontent == NULL)
>> + return NULL;
>> +
>> + fetchcache_go(icocontent, 0, search_web_ico_callback,
>> + 0, 0, 20, 20,
>> + 0, 0, false, 0);
>> +
>> + if (icocontent == NULL) {
>> + LOG(("web search ico loading delayed"));
>> + return NULL;
>> + }
>>
> This is redundant, no?
>
>> + return icocontent;
>> +}
>> +
>> +void search_web_ico_callback(content_msg msg, struct content *ico,
>> + intptr_t p1, intptr_t p2, union content_msg_data data)
>>
> Documentation!
>
>> + case CONTENT_MSG_DONE:
>> + LOG(("got favicon '%s'", ico->url));
>> + if (ico->type == CONTENT_ICO) {
>> + search_ico = ico; /* cache */
>> + gui_window_set_search_ico();
>>
> Set it to what? Please tell me it doesn't expect to extract it from
> search_ico.
>
well the reason search_ico is global rather than being passed is that it
may be cached for asynchronous updates of the front
>> Index: desktop/searchweb.h
>> ===================================================================
>> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
>> +++ desktop/searchweb.h 2009-07-10 12:50:17.000000000 +0100
>> +typedef enum {
>> + OSP_GOOGLE,
>> + OSP_YAHOO,
>> + OSP_MICROSOFT,
>> + OSP_BUSINESS,
>> + OSP_OMGILI,
>> + OSP_BBC,
>> + OSP_UBUNTU,
>> + OSP_COMMONS,
>> + OSP_ASK,
>> + OSP_ANSWERS,
>> + OSP_DICTIONARYCOM,
>> + OSP_YOUTUBE,
>> + OSP_AEROMP3,
>> + OSP_AOL,
>> + OSP_BAIDU,
>> + OSP_AMAZON,
>> + OSP_EBAY,
>> + OSP_IMDB,
>> + OSP_ESPN,
>> + OSP_WIKIPEDIA
>> +} default_search_provider;
>>
> So there is a search provider enum. What is OSP? Does this need to be
> public?
>
>> +struct content *search_web_retrieve_ico(bool localdefault);
>> +
>> +void search_web_ico_callback(content_msg msg, struct content *ico,
>> + intptr_t p1, intptr_t p2, union content_msg_data data);
>>
> Why is this public?
>
>> Index: desktop/search.c
>> ===================================================================
>> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
>> +++ desktop/search.c 2009-07-10 12:50:19.000000000 +0100
>>
>> +struct browser_window *search_current_window = NULL;
>> +static char *search_string = NULL;
>> +static struct list_entry search_head = { 0, 0, NULL, NULL, NULL, NULL, NULL };
>> +static struct list_entry *search_found = &search_head;
>> +static struct list_entry *search_current = NULL;
>> +static struct content *search_content = NULL;
>> +static bool search_prev_case_sens = false;
>> +bool search_insert;
>> +char *recent_search[RECENT_SEARCHES];
>>
> It would be significantly better if there were a search object that
> contained all the above data. You'd have one such object per search
> context (i.e. one per window or tab).
>
> You'd need some API to create and destroy such objects as well as
> changes to all the other APIs to pass the search context around.
>
definitely; as I say I'd rather avoid breaking what worked until it's
time for optimization; besides the review is sufficiently monolithic
already isn't it?
>> +/**
>> + * Begins/continues the search process
>> + * Note that this may be called many times for a single search.
>> + *
>> + * \param forwards search forwards from start/current position
>> + */
>> +
>> +void start_search(bool forwards)
>> +{
>> + int string_len;
>> + const char *string;
>> + int i = 0;
>> +
>> + string = gui_search_get_string();
>> + assert(string);
>>
> Can the frontend not pass this in as a parameter?
>
it could; I'm avoiding tampering with it though until it's time for
optimization
>> + gui_search_add_recent(string);
>>
> What's this for?
>
legacy from riscos; could be optimized
>> +/**
>> + * Search for a string in the box tree
>> + *
>> + * \param string the string to search for
>> + * \param string_len length of search string
>> + * \param case_sens whether to perform a case sensitive search
>> + * \param forwards direction to search in
>> + */
>> +void do_search(const char *string, int string_len, bool case_sens,
>> + bool forwards)
>> +{
>> + /* check if we need to start a new search or continue an old one */
>> + if (!search_string || c != search_content || !search_found->next ||
>> + search_prev_case_sens != case_sens ||
>> + (case_sens && strcmp(string, search_string) != 0) ||
>> + (!case_sens && strcasecmp(string, search_string) != 0)) {
>>
>
> Dear lord this is awful. The frontend presumably knows this already, so
> can pass it in as a parameter, rather than have this hideous,
> potentially broken, mess.
>
ask the bloke who wrote the riscos implementation, what was his name? :-)
>> + bool res;
>> +
>> + if (search_string)
>> + free(search_string);
>> + search_current = 0;
>> + free_matches();
>> +
>> + search_string = malloc(string_len + 1);
>> + if (search_string) {
>>
> != NULL.
>
>> + gui_search_set_hourglass(true);
>>
> I've already commented on this call.
>
>> +/**
>> + * Determines whether any portion of the given text box should be
>> + * selected because it matches the current search string.
>> + *
>> + * \param g gui window
>> + * \param start_offset byte offset within text of string to be checked
>> + * \param end_offset byte offset within text
>> + * \param start_idx byte offset within string of highlight start
>> + * \param end_idx byte offset of highlight end
>> + * \return true iff part of the box should be highlighted
>> + */
>> +
>> +bool gui_search_term_highlighted(struct gui_window *g,
>> + unsigned start_offset, unsigned end_offset,
>> + unsigned *start_idx, unsigned *end_idx)
>>
> If this is core, it shouldn't start with gui_.
>
>> Index: desktop/search.h
>> ===================================================================
>> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
>> +++ desktop/search.h 2009-07-10 12:50:19.000000000 +0100
>> +#define RECENT_SEARCHES 8
>> +
>> +extern char *recent_search[RECENT_SEARCHES];
>> +extern bool search_insert;
>>
> These need to die.
>
>> Index: desktop/save_complete.c
>> ===================================================================
>> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
>> +++ desktop/save_complete.c 2009-07-10 12:50:20.000000000 +0100
>> +#ifdef RISCOS
>> + static char pathsep = '.';
>> +#else
>> + static char pathsep = '/';
>> +#endif
>>
> I don't like this. Nor, for that matter, do I understand why it exists.
>
save_complete_inventory needs it
>> +/** List of urls seen and saved so far. */
>> +static struct save_complete_entry *save_complete_list = 0;
>>
> I'd prefer this to not be global. Stick it on the stack in the top-level
> call, and pass a pointer to it around internally.
>
optimization? Honestly I'd rather that the original writer of the search
code modified it as it looks kind of delicate to me
>> Index: desktop/save_complete.h
>> ===================================================================
>> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
>> +++ desktop/save_complete.h 2009-07-10 12:50:22.000000000 +0100
>> +/**
>> + * conducts the filesystem save appropriate to the gui
>> + * \param path save path
>> + * \param filename name of file to save
>> + * \param c content to save, or NULL
>> + * \param len data length
>> + * \param sourcedata pointer to data to save, NULL when all data in sourcedata
>> + * \param type integer filetype [riscos]
>> + * \return true for success
>> + */
>> +bool save_complete_gui_save(const char *path, const char *filename, struct content *c, int len, char *sourcedata, int type);
>>
> 80 columns.
>
>> Index: render/html.h
>> ===================================================================
>> --- render/html.h (revision 8438)
>> +++ render/html.h (working copy)
>> @@ -128,6 +128,8 @@
>> colour background_colour; /**< Document background colour. */
>> const struct font_functions *font_func;
>>
>> + struct content *favicon;
>> +
>>
> Documentation?
>
>> Index: image/ico.c
>> ===================================================================
>> --- image/ico.c (revision 8438)
>> +++ image/ico.c (working copy)
>> @@ -114,6 +114,15 @@
>> background_colour, BITMAPF_NONE);
>> }
>>
>> +bool nsico_set_bitmap_from_size(struct content *c, int width, int height)
>>
> Documentation?
>
>> +{
>> + struct bmp_image *bmp = ico_find(c->data.ico.ico, width, height);
>>
> Failure?
>
>> + if (!bmp->decoded)
>>
> == false.
>
>> + if (bmp_decode(bmp) != BMP_OK)
>>
> Can be merged with parent "if" through use of &&.
>
>> Index: gtk/gtk_gui.h
>> ===================================================================
>> --- gtk/gtk_gui.h (revision 8438)
>> +++ gtk/gtk_gui.h (working copy)
>> @@ -25,8 +25,18 @@
>> #include <glade/glade.h>
>>
>> extern bool gui_in_multitask;
>> -extern GladeXML *gladeWindows;
>> -extern char *glade_file_location;
>> +extern GladeXML *gladeNetsurf;
>> +extern GladeXML *gladePassword;
>> +extern GladeXML *gladeWarning;
>> +extern GladeXML *gladeLogin;
>> +extern GladeXML *gladeSsl;
>>
> Do these things actually need to be global?
>
as far as I know they do; various aspects of the gtk front need access
to them
>> +extern char *glade_netsurf_file_location;
>> +extern char *glade_password_file_location;
>> +extern char *glade_warning_file_location;
>> +extern char *glade_login_file_location;
>> +extern char *glade_ssl_file_location;
>> +extern char *glade_toolbar_file_location;
>> +extern char *toolbar_indices_file_location;
>> extern char *options_file_location;
>> extern char *res_dir_location;
>> extern char *print_options_file_location;
>>
> Ditto.
>
>> Index: gtk/gtk_window.c
>> ===================================================================
>> --- gtk/gtk_window.c (revision 8438)
>> +++ gtk/gtk_window.c (working copy)
>> @@ -35,6 +36,37 @@
>> #include <gdk/gdkkeysyms.h>
>> #include <assert.h>
>>
>> +struct gui_window {
>> + /* All gui_window objects have an ultimate scaffold */
>> + nsgtk_scaffolding *scaffold;
>> + /* A gui_window is the rendering of a browser_window */
>> + struct browser_window *bw;
>> + struct browser_mouse *mouse;
>> +
>> + /* These are the storage for the rendering */
>> + int caretx, carety, careth;
>> + gui_pointer_shape current_pointer;
>> + int last_x, last_y;
>> +
>> + /* Within GTK, a gui_window is a scrolled window
>> + * with a viewport inside
>> + * with a gtkfixed in that
>> + * with a drawing area in that
>> + * The scrolled window is optional and only chosen
>> + * for frames which need it. Otherwise we just use
>> + * a viewport.
>> + */
>> + GtkWidget *tab;
>> + GtkScrolledWindow *scrolledwindow;
>> + GtkViewport *viewport;
>> + GtkFixed *fixed;
>> + GtkDrawingArea *drawing_area;
>> + gulong signalhandler[2];
>>
> What is this for? Why 2?
>
one for window clicks, one for repaints
>> +
>> + /* Keep gui_windows in a list for cleanup later */
>> + struct gui_window *next, *prev;
>> +};
>>
> All fields need documenting separately.
>
I simply moved them
>> struct gui_window *window_list = 0; /**< first entry in win list*/
>>
> NULL.
>
ditto
>> Index: gtk/gtk_scaffolding.c
>> ===================================================================
>> --- gtk/gtk_scaffolding.c (revision 8438)
>> +++ gtk/gtk_scaffolding.c (working copy)
>> +nsgtk_scaffolding *scaf_list = NULL; /**< global list for interface changes */
>>
> This can be named better.
>
>> +void nsgtk_window_close(struct gtk_scaffolding *g)
>> {
>> - struct gtk_scaffolding *g = data;
>> + /* close all tabs first */
>> + gint numbertabs = gtk_notebook_get_n_pages(g->notebook);
>> + while (numbertabs-- > 1) {
>> + nsgtk_tab_close_current(g->notebook);
>> + }
>> LOG(("Being Destroyed = %d", g->being_destroyed));
>> - if (g->history_window->window) {
>> +/* if ((g->history_window) && (g->history_window->window)) {
>> gtk_widget_destroy(GTK_WIDGET(g->history_window->window));
>> }
>> - gtk_widget_destroy(GTK_WIDGET(g->window));
>> -
>> + if (g->window)
>> + gtk_widget_destroy(GTK_WIDGET(g->window));
>> +*/
>>
> Why is the above commented out?
>
it looks as though it is code that I had to comment when I changed the
logic so that it was reached at all [the gtk bug]; in view of the core
widgets business I left it there as an indicator for now; comment added
>> if (--open_windows == 0)
>> netsurf_quit = true;
>>
>> @@ -285,38 +236,38 @@
>> g->being_destroyed = 1;
>> nsgtk_window_destroy_browser(g->top_level);
>> }
>> + if (g->prev)
>>
> != NULL.
>
>> + g->prev->next = g->next;
>> + else
>> + scaf_list = g->next;
>> +
>> + if (g->next)
>>
> Ditto.
>
>> static gboolean nsgtk_window_edit_menu_clicked(GtkWidget *widget,
>> struct gtk_scaffolding *g)
>> {
>> - nsgtk_scaffolding_update_edit_actions_sensitivity (g, g->xml, FALSE);
>> + nsgtk_scaffolding_update_edit_actions_sensitivity(g, g->xml, false);
>>
> Is the third parameter a bool or a gboolean? If the latter, then it
> should be FALSE.
it's a bool
>> gboolean nsgtk_window_url_activate_event(GtkWidget *widget, gpointer data)
>> {
>> struct gtk_scaffolding *g = data;
>> - struct browser_window *bw = nsgtk_get_browser_for_gui(g->top_level);
>> + struct browser_window *bw = gui_window_get_browser_window(g->top_level);
>> + char *url = (char *)gtk_entry_get_text(GTK_ENTRY(g->url_bar));
>>
> Does this return an allocated object or a pointer to constant data?
>
const; however as I reassign url as the result of a function it should
be good
>> + if (!search_is_url(url))
>>
> == false.
>
>> + url = search_web_from_term(url);
>>
> This returns an allocated object, no?
>
>> + browser_window_go(bw, url, 0, true);
>>
>> - browser_window_go(bw, gtk_entry_get_text(GTK_ENTRY(g->url_bar)),
>> - 0, true);
>> -
>>
> So, in one of the possible routes through this function, you're leaking
> memory.
>
possibly although search_is_url() is very partial in its implementation;
modified
>> Index: gtk/dialogs/gtk_options.c
>> ===================================================================
>> --- gtk/dialogs/gtk_options.c (revision 8438)
>> +++ gtk/dialogs/gtk_options.c (working copy)
>> void nsgtk_options_load(void)
>> {
>> + /* populate theme combo from themelist file */
>> + box = GTK_BOX(glade_xml_get_widget(gladeFile, "themehbox"));
>> + combotheme = gtk_combo_box_new_text();
>>
> Failure?
>
>> + themefile = g_strconcat(res_dir_location, "themelist", NULL);
>>
> Ditto.
>
>> + fp = fopen((const char *)themefile, "r");
>> + g_free(themefile);
>> + if (fp == NULL) {
>> + LOG(("Failed opening themes file"));
>> + warn_user("FileError", (const char *) themefile);
>> + return;
>> + }
>> + while (fgets(buf, sizeof(buf), fp)) {
>>
> != NULL.
>
>> +COMBO_CHANGED(comboSearch, option_search_provider)
>> + nsgtk_scaffolding *current = scaf_list;
>> + char *name;
>> + /* refresh web search prefs from file */
>> + search_web_provider_details(option_search_provider);
>> + /* retrieve ico */
>> + search_ico = search_web_retrieve_ico(false);
>> + /* callback may handle changing gui */
>> + if (search_ico != NULL)
>> + gui_window_set_search_ico();
>> + /* set entry */
>> + name = search_web_provider_name();
>>
> Failure?
>
>> + while (current) {
>> + nsgtk_scaffolding_set_websearch(current, name);
>> + current = nsgtk_scaffolding_iterate(current);
>> + }
>> + free(name);
>> +END_HANDLER
>> +
>> +COMBO_CHANGED(combotheme, option_current_theme)
>> + nsgtk_scaffolding *current = scaf_list;
>> + if (option_current_theme != 0) {
>> + if (current_theme_name != NULL)
>> + free(current_theme_name);
>> + current_theme_name = strdup(gtk_combo_box_get_active_text(
>> + GTK_COMBO_BOX(combotheme)));
>>
> Failure?
>
>> +BUTTON_CLICKED(buttonaddtheme)
>> + char *themesfolder, *filename, *directory;
>> + GtkWidget *fc = gtk_file_chooser_dialog_new(
>> + messages_get("gtkAddThemeTitle"),
>> + GTK_WINDOW(wndPreferences),
>> + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
>> + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
>> + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
>> + themesfolder = g_strconcat(res_dir_location, "themes", NULL);
>>
> Failure?
>
>> Index: gtk/dialogs/gtk_options.h
>> ===================================================================
>> --- gtk/dialogs/gtk_options.h (revision 8438)
>> +++ gtk/dialogs/gtk_options.h (working copy)
>> GtkDialog* nsgtk_options_init(struct browser_window *bw, GtkWindow *parent); /** Init options and load window */
>> void nsgtk_options_load(void); /** Load current options into window */
>> void nsgtk_options_save(void); /** Save options from window */
>> +bool nsgtk_options_combo_theme_add(const char *themename); /** add new theme name to combo */
>>
> 80 columns.
>
>> Index: gtk/gtk_gui.c
>> ===================================================================
>> --- gtk/gtk_gui.c (revision 8438)
>> +++ gtk/gtk_gui.c (working copy)
>> @@ -67,15 +67,27 @@
>> char *default_stylesheet_url;
>> char *adblock_stylesheet_url;
>> char *options_file_location;
>> -char *glade_file_location;
>> +char *glade_netsurf_file_location;
>> +char *glade_password_file_location;
>> +char *glade_warning_file_location;
>> +char *glade_login_file_location;
>> +char *glade_ssl_file_location;
>> +char *glade_toolbar_file_location;
>> +char *toolbar_indices_file_location;
>> char *res_dir_location;
>> char *print_options_file_location;
>> +char *search_engines_file_location;
>> +char *search_default_ico_location;
>>
>> -struct gui_window *search_current_window = 0;
>> +/* struct gui_window *search_current_window = 0; */
>>
> Why is this commented out? If it's redundant, delete it.
>
>> Index: Docs/Doxyfile
>> ===================================================================
>> --- Docs/Doxyfile (revision 8438)
>> +++ Docs/Doxyfile (working copy)
>> @@ -896,6 +896,8 @@
>>
>> PREDEFINED = riscos CSS_INTERNALS WITH_ARTWORKS WITH_BMP WITH_DRAW WITH_DRAW_EXPORT WITH_GIF WITH_JPEG WITH_MMAP WITH_MNG WITH_NSSPRITE WITH_NS_SVG WITH_PLUGIN WITH_RSVG WITH_SAVE_COMPLETE WITH_SPRITE WITH_THEME_INSTALL WITH_PDF_EXPORT
>>
>> +PREDEFINED = gtk WITH_THEME_INSTALL
>> +
>>
> Does this add to the previous line, or override it?
>
adds to it from my reading of the documentation; needs the gtk at the
front of the string as I understand it
>> Index: utils/utils.c
>> ===================================================================
>> --- utils/utils.c (revision 8438)
>> +++ utils/utils.c (working copy)
>> @@ -61,6 +61,23 @@
>> return 1;
>> }
>>
>> +char *remove_underscores(const char *s, bool replacespace)
>>
> Documentation.
>
>> +{
>> + size_t i, len, offset = 0;
>> + char *ret;
>> + len = strlen(s);
>> + ret = malloc(len + 1);
>>
> Failure?
>
modified
>> + for (i = 0; i < len; i++)
>> + if (s[i] != '_')
>> + ret[i - offset] = s[i];
>> + else if (replacespace)
>> + ret[i - offset] = ' ';
>> + else
>> + offset++;
>> + ret[i - offset] = '\0';
>>
> i - offset isn't particularly clear. What's wrong with maintaining an
> input buffer index (i) and an output buffer index?
>
2 indices slows it down?
>> Index: utils/container.c
>> ===================================================================
>> --- utils/container.c (revision 8438)
>> +++ utils/container.c (working copy)
>> strncpy((char *)ctx->directory[ctx->entries - 1].filename,
>> - (char *)entryname, 16);
>> + (char *)entryname, 64);
>>
> use sizeof()
>
it all looks a bit delicate to me; I'd rather the original writer
optimized it when he has time
>> struct container_ctx *container_open(const char *filename)
>> {
>> + size_t val;
>> struct container_ctx *ctx = calloc(sizeof(struct container_ctx), 1);
>>
>> ctx->fh = fopen(filename, "rb");
>>
>> if (ctx->fh == NULL) {
>> free(ctx);
>> - return NULL;
>> + return NULL;
>>
> Spurious whitespace change.
>
It does look that way :-)
>> +#ifdef WITH_THEME_INSTALL
>> +
>> +/**
>> + * install theme from container
>> + * \param themefile a file containing the containerized theme
>> + * \param dirbasename a directory basename including trailing path sep; the
>> + * full path of the theme is then a subdirectory of that
>> + */
>> +
>> +char *container_extract_theme(const char *themefile, const char *dirbasename)
>> +{
>> + struct stat statbuf;
>> + struct container_ctx *cctx;
>> + FILE *fh;
>> + size_t val;
>> + const unsigned char *e, *d;
>> + char *themename, *dirname;
>> + char path[PATH_MAX];
>> + int state = 0;
>> + unsigned int i;
>> + u_int32_t flen;
>> +
>> + cctx = container_open(themefile);
>> + if (cctx == NULL) {
>> + warn_user("FileOpenError", themefile);
>> + return NULL;
>> + }
>> + themename = (char *)container_get_name(cctx);
>> + LOG(("theme name: %s", themename));
>> + LOG(("theme author: %s", container_get_author(cctx)));
>> +
>> + dirname = malloc(strlen(dirbasename) + strlen(themename) + 2);
>>
> Failure?
>
>> Index: riscos/save.c
>> ===================================================================
>> --- riscos/save.c (revision 8438)
>> +++ riscos/save.c (working copy)
>> +bool save_complete_gui_save(const char *path, const char *filename, struct
>> + content *c, int len, char *sourcedata, int type)
>> +{
>> + char *finame;
>> + int namelen = strlen(path) + strlen(filename) + 2;
>> + finame = malloc(namelen);
>> + if (!finame) {
>>
> == NULL.
>
>> +int save_complete_htmlSaveFileFormat(const char *path, const char *filename,
>> + xmlDocPtr cur, const char *encoding, int format)
>> +{
>> + int ret;
>> + int len = strlen(path) + strlen(filename) + 2;
>> + char *finame = malloc(len);
>> + if (!finame){
>>
> == NULL.
>
>> +bool save_complete_gui_filetype(const char *path, const char *filename, int type)
>> +{
>> + os_error *error;
>> + int len = strlen(path) + strlen(filename) + 2;
>> + char *finame = malloc(len);
>> + if (!finame){
>>
> == NULL.
>
>> Index: desktop/browser.c
>> ===================================================================
>> --- desktop/browser.c (revision 8438)
>> +++ desktop/browser.c (working copy)
>> @@ -167,6 +168,23 @@
>> if (url)
>> browser_window_go(bw, url, referer, history_add);
>>
>> + else {
>> + LOG(("creating blank content"));
>> + struct content *c = content_create(" ");
>>
> Failure?
>
>> + const char *params[] = { 0 };
>> + int length;
>> + const char *blankcontent = "<html><head><title>blank page \
>> + </title></head><body></body></html>";
>> + length = strlen(blankcontent);
>> + if (content_set_type(c, CONTENT_HTML, "text/html", params)
>> + && content_process_data(c, blankcontent,
>> + length)) {
>> + /* here need to retrieve width, height from browser */
>> + content_convert(c, c->width, c->height);
>> + c->fresh = false;
>> + }
>> + }
>>
> I'm not sure I see the point of this, tbh.
>
well it would be nice once it is working properly
>> Index: desktop/browser.h
>> ===================================================================
>> --- desktop/browser.h (revision 8438)
>> +++ desktop/browser.h (working copy)
>> @@ -213,6 +213,7 @@
>>
>>
>> extern struct browser_window *current_redraw_browser;
>> +extern struct browser_window *search_current_window;
>>
> Why is this here? It's search specific.
>
well it's a browser_window :-) you're the boss
>> Index: content/fetchcache.c
>> ===================================================================
>> --- content/fetchcache.c (revision 8438)
>> +++ content/fetchcache.c (working copy)
>> -void fetchcache_error_page(struct content *c, const char *error)
>> +void fetchcache_error_page(struct content *c, const char
>> + *error)
>>
> What's with the spurious line break?
>
it must have got in incognito :-D
>> {
>> const char *params[] = { 0 };
>> int length;
>> + char *host;
>> + char checkmessage[24];
>>
>> + if (option_search_url_bar)
>> + /* get the start of the error message for comparison
>> + * the potential error message we're identifying is derived
>> + * directly from libcurl, so hopefully no need for I18n */
>> + snprintf(checkmessage, 24, "%s", error);
>> + if (((url_host(c->url, &host) != URL_FUNC_OK) || (strcasecmp(host,
>> + search_web_provider_host())!= 0)) &&
>> + (strcasecmp(checkmessage, "couldn't resolve host '")
>> + == 0)) {
>> + fetchcache_search_redirect(c, error);
>> + return;
>> + }
>>
> I really, really dislike this. Performing such string comparisons is
> liable to unexpected failure should the error message change.
>
however unless there's a better suggestion at least it works for now;
one more for the optimization queue I'd say
> checkmessage is never initialised if option_search_url_bar is false.
>
> You leak host, too.
>
>> +void fetchcache_search_redirect(struct content *c, const char *error)
>> +{
>> + char *redirurl, *temp;
>> + bool can_fetch;
>> + union content_msg_data msg_data;
>>
>> + /* clear http:// plus trailing / from url, it is already escaped */
>> + temp = strdup(c->url + SLEN("http://"));
>>
> Failure?
>
>> + temp[strlen(temp)-1] = '\0';
>> + redirurl = search_web_get_url(temp);
>>
> Ditto.
>
>> + /* logic borrowed from fetchcache_redirect() */
>>
> What's preventing you calling fetchcache_redirect()?
>
there are minor modifications :-)
> J.
>
Once more thanks for making time I really understand how much of a
monolithic task it it I hope my occasional mildly cheeky comments are
taken in good spirit :-D
Best regards
Mark
http://www.halloit.com
Key ID 046B65CF
14 years, 1 month
Review: Bo Yang -- LibDOM Core (round 2)
by John-Mark Bell
Precis:
This is round 2 of Bo Yang's libdom core changes.
Added files
Index: test/transform.pl
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ test/transform.pl 2009-07-30 11:27:55.000000000 +0100
@@ -0,0 +1,21 @@
+#!/bin/perl
+# This file is part of libdom.
+# It is used to generate libdom test files from the W3C DOMTS.
+#
+# Licensed under the MIT License,
+# http://www.opensource.org/licenses/mit-license.php
+# Author: Bo Yang <struggleyb.nku(a)gmail.com>
+
+use warnings;
+use strict;
+
+use XML::Parser::PerlSAX;
+use DOMTSHandler;
+
+if ($#ARGV ne 1) {
+ die "Usage: perl transform.pl dtd-file testcase-file";
+}
+
+my $handler = DOMTSHandler->new($ARGV[0]);
+my $parser = XML::Parser::PerlSAX->new(Handler => $handler);
+$parser->parse(Source => {SystemId => "$ARGV[1]"});
Index: test/run-test.sh
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ test/run-test.sh 2009-07-30 11:27:55.000000000 +0100
@@ -0,0 +1,146 @@
+#!/bin/bash
+#
+# This is a simple script to convert the XML testcaes to C source file, compile it, run it, and report the result.
+# Usage:
+# This script is designed to run under the libdom/test directory.
+#
+# domts="testcases" dtd="dom1-interfaces.xml" level="level1" ./run-test.sh
+#
+# The above command will convert the XML testcase in directory testcases/tests/level/core and
+# use dom1-interfaces.xml to convert it.
+# This script will generate a output/ directory in libdom/test, and in that directory, there is a same structure
+# as in DOMTS XML testcases files.
+#
+# This file is part of libdom test suite.
+# Licensed under the MIT License,
+# http://www.opensource.org/licenses/mit-license.php
+# Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+
+level=${level:-"level1"};
+module=${module:-"core"};
+domts=${domts:?"The \$domts must be assigned some value"};
+output=${output:-"output"};
+dtd=${dtd:?"The DTD file must be given"};
+
+testdir="$domts"/tests/"$level"/"$module"
+log="$output"/"$level"/"$module"/test.log;
+
+src="testutils/comparators.c testutils/domtsasserts.c testutils/foreach.c testutils/list.c testutils/load.c testutils/utils.c testutils/domtscondition.c"
+domdir="../build-Linux-Linux-debug-lib-static"
+ldflags="-L$domdir -ldom -L/usr/local/lib -lwapcaplet -L/usr/lib -lxml2 -lhubbub -lparserutils"
+#ldflags="-L/usr/lib -lm -lz -L$domdir -ldom -L/usr/local/lib -lwapcaplet -lxml2 -lhubbub -lparserutils"
+cflags="-Itestutils/ -I../bindings/xml -I../include -I../bindings/hubbub -I/usr/local/include"
+
+total=0;
+fail=0;
+pass=0;
+conversion=0;
+compile=0;
+run=0;
+nsupport=0;
+
+# Create the directories if necessary
+if [ ! -e "$ouput" ]; then
+ mkdir -p "$output";
+fi
+if [ ! -e "$level" ]; then
+ mkdir -p "$output"/"$level";
+fi
+if [ ! -e "$module" ]; then
+ mkdir -p "$output"/"$level"/"$module";
+fi
+
+# Prepare the test files
+if [ -e "files" ]; then
+ rm -fr files;
+fi
+cp -fr "$testdir/files" ./;
+
+while read test; do
+ total=$(($total+1));
+
+ file=`basename "$test"`;
+ name=${file%%.xml};
+
+ cfile="$output"/"$level"/"$module"/"$name".c;
+ ofile="$output"/"$level"/"$module"/"$name";
+ tfile="$testdir"/"$file";
+
+ echo -n "$file:";
+
+ # Generate the test C program
+ out=`perl transform.pl "$dtd" "$tfile" 2>&1 >"${cfile}.unindent"`;
+ if [ "$?" != "0" ]; then
+ fail=$((fail+1));
+ conversion=$((conversion+1));
+ echo "$tfile Conversion Error:" >& 3;
+ echo "Please make sure you have XML::XPath perl module installed!" >& 3;
+ echo "$out" >&3;
+ echo -e "----------------------------------------\n\n" >& 3;
+ echo "failed!";
+ rm -fr "${cfile}.unindent";
+ continue;
+ fi
+ out=`indent "${cfile}.unindent" -o "$cfile" 2>&1`;
+ if [ "$?" != "0" ]; then
+ rm -fr "${cfile}.unindent";
+ fail=$((fail+1));
+ conversion=$((conversion+1));
+ echo "$tfile Conversion Error:" >& 3;
+ echo "$out" >& 3;
+ echo -e "----------------------------------------\n\n" >& 3;
+ echo "failed!";
+ continue;
+ fi
+ rm -fr "${cfile}.unindent";
+
+ # Compile it now
+ out=` ( gcc -g $cflags $src $cfile $ldflags -o "$ofile" ) 2>&1`;
+ if [ "$?" != "0" ]; then
+ fail=$((fail+1));
+ compile=$((compile+1));
+ echo "$tfile Compile Error:" >& 3;
+ echo "$out" >& 3;
+ echo -e "----------------------------------------\n\n" >& 3;
+ echo "failed!";
+ continue;
+ fi
+
+ # Run the test and test the result
+ cd files;
+ out=$(../$ofile 2>&1);
+ ret="$?";
+ if [ "$ret" != "0" ]; then
+ cd ..;
+ fail=$((fail+1));
+ if [ "$ret" == "9" ]; then
+ nsupport=$((nsupport+1))
+ echo "$tfile Not Support:" >& 3;
+ echo "$out" >& 3;
+ echo -e "----------------------------------------\n\n" >& 3;
+ echo "not supported!";
+ else
+ run=$((run+1));
+ echo "$tfile Run Error:" >& 3;
+ echo "$out" >& 3;
+ echo -e "----------------------------------------\n\n" >& 3;
+ echo "failed!";
+ fi
+ continue;
+ fi
+ cd ..;
+
+ pass=$((pass+1));
+ echo "passed.";
+
+done 3>"$log" < <(perl -ne 'if ($_ =~ /href=\"(.*\.xml)\"/) { print "$1\n"; }' -- "$testdir"/alltests.xml);
+
+echo "Total: $total" | tee >>"$log";
+echo "Passed: $pass" | tee >>"$log";
+echo "Failed: $fail" | tee >>"$log";
+echo "Conversion Error: $conversion" | tee >>"$log";
+echo "Compile Error: $compile" | tee >>"$log";
+echo "Run Error: $run" | tee >>"$log";
+echo "Not Support: $nsupport" | tee >>"$log";
+
+cat "$log";
Index: test/README
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ test/README 2009-07-30 11:27:55.000000000 +0100
@@ -0,0 +1,2 @@
+Note the lib/ directory contain the old test related files, although they
+are not workable, I leave them here for later use if there is a chance.
Index: test/DOMTSHandler.pm
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ test/DOMTSHandler.pm 2009-07-30 11:27:55.000000000 +0100
@@ -0,0 +1,1541 @@
+# This is the PerSAX Handlers Package
+
+package DOMTSHandler;
+
+use Switch;
+
+use XML::XPath;
+use XML::XPath::XMLParser;
+
+our $description = 0;
+our $string_index = 0;
+our $ret_index = 0;
+our $condition_index = 0;
+our $test_index = 0;
+our $iterator_index = 0;
+our $temp_index = 0;
+# Sometimes, we need temp nodes
+our $tnode_index = 0;
+our $dom_feature = "\"XML\"";
+our %bootstrap_api = (
+ dom_implementation_create_document_type => "",
+ dom_implementation_create_document => "",
+);
+our %native_interface = (
+ DOMString => \&generate_domstring_interface,
+ DOMTimeStamp => "",
+ DOMUserData => "",
+ DOMObject =>"",
+);
+our %special_type = (
+ # Some of the type are not defined now!
+ boolean => "bool ",
+ int => "unsigned long ",
+ "unsigned long" => "unsigned long ",
+ DOMString => "dom_string *",
+ List => "list *",
+ Collection => "list *",
+ DOMImplementation => "dom_implementation *",
+ NamedNodeMap => "dom_namednodemap *",
+ NodeList => "dom_nodelist *",
+ CharacterData => "dom_characterdata *",
+ CDATASection => "dom_cdata_section *",
+);
+our %special_prefix = (
+ DOMString => "dom_string",
+ DOMImplementation => "dom_implementation",
+ NamedNodeMap => "dom_namednodemap",
+ NodeList => "dom_nodelist",
+ CharacterData => "dom_characterdata",
+ CDATASection => "dom_cdata_section *",
+);
+
+our %unref_prefix = (
+ DOMString => "dom_string",
+ NamedNodeMap => "dom_namednodemap",
+ NodeList => "dom_nodelist",
+);
+
+our %special_method = (
+);
+
+our %special_attribute = (
+ namespaceURI => "namespace",
+);
+
+our %no_unref = (
+ "boolean" => 1,
+ "int" => 1,
+ "unsigned int" => 1,
+ "List" => 1,
+ "Collection" => 1,
+);
+
+our %override_suffix = (
+ boolean => "bool",
+ int => "int",
+ "unsigned long" => "unsigned_long",
+ DOMString => "domstring",
+ DOMImplementation => "domimplementation",
+ NamedNodeMap => "domnamednodemap",
+ NodeList => "domnodelist",
+ Collection => "list",
+ List => "list",
+);
+
+our %exceptions = (
+
+ DOM_NO_ERR => 0,
+ DOM_INDEX_SIZE_ERR => 1,
+ DOM_DOMSTRING_SIZE_ERR => 2,
+ DOM_HIERARCHY_REQUEST_ERR => 3,
+ DOM_WRONG_DOCUMENT_ERR => 4,
+ DOM_INVALID_CHARACTER_ERR => 5,
+ DOM_NO_DATA_ALLOWED_ERR => 6,
+ DOM_NO_MODIFICATION_ALLOWED_ERR => 7,
+ DOM_NOT_FOUND_ERR => 8,
+ DOM_NOT_SUPPORTED_ERR => 9,
+ DOM_INUSE_ATTRIBUTE_ERR => 10,
+ DOM_INVALID_STATE_ERR => 11,
+ DOM_SYNTAX_ERR => 12,
+ DOM_INVALID_MODIFICATION_ERR => 13,
+ DOM_NAMESPACE_ERR => 14,
+ DOM_INVALID_ACCESS_ERR => 15,
+ DOM_VALIDATION_ERR => 16,
+ DOM_TYPE_MISMATCH_ERR => 17,
+ DOM_NO_MEM_ERR => (1<<16)
+);
+
+our @condition = qw(same equals notEquals less lessOrEquals greater greaterOrEquals isNull notNull and or xor not instanceOf isTrue isFalse hasSize contentType hasFeature implementationAttribute);
+
+our @exception = qw(INDEX_SIZE_ERR DOMSTRING_SIZE_ERR HIERARCHY_REQUEST_ERR WRONG_DOCUMENT_ERR INVALID_CHARACTER_ERR NO_DATA_ALLOWED_ERR NO_MODIFICATION_ALLOWED_ERR NOT_FOUND_ERR NOT_SUPPORTED_ERR INUSE_ATTRIBUTE_ERR NAMESPACE_ERR);
+
+our @assertion = qw(assertTrue assertFalse assertNull assertNotNull assertEquals assertNotEquals assertSame assertInstanceOf assertSize assertEventCount assertURIEquals);
+
+our @control = qw(if while for-each else);
+
+our @framework_statement = qw(assign increment decrement append plus subtract mult divide load implementation comment hasFeature implementationAttribute EventMonitor.setUserObj EventMonitor.getAtEvents EventMonitor.getCaptureEvents EventMonitor.getBubbleEvents EventMonitor.getAllEvents wait);
+
+sub new {
+ my $type = shift;
+ my $dtd = shift;
+ my $dd = XML::XPath->new(filename => $dtd);
+ my $self = {
+ # The DTD file of the xml files
+ dd => $dd,
+ # To indicate whether we are in comments
+ comment => 0,
+ # To indicate that whether we are in <comment> element
+ inline_comment => 0,
+ # The stack of elements encountered utill now
+ context => [],
+ # The map for <var> name => type
+ var => {},
+ # See the comment on generate_condition2 for this member
+ condition_stack => [],
+ # The list for UNREF
+ unref => [],
+ string_unref => [],
+ # The indent of current statement
+ indent => "",
+ # The variables for List/Collection
+ # We now, declare an array for a list and then add them into a list
+ # The map for all the List/Collection in one test
+ # "List Name" => "Member type"
+ list_map => {},
+ # The name of the current List/Collection
+ list_name => "",
+ # The number of items of the current List/Collection
+ list_num => 0,
+ # Whether List/Collection has members
+ list_hasmem => 0,
+ # The type of the current List/Collection
+ list_type => "",
+ # Whether we are in exception assertion
+ exception => 0,
+
+ };
+
+ return bless $self, $type;
+}
+
+sub start_element {
+ my ($self, $element) = @_;
+
+ my $en = $element->{Name};
+
+ my $dd = $self->{dd};
+ my $ct = $self->{context};
+ push(@$ct, $en);
+
+ switch ($en) {
+ case "test" {
+ ;
+ }
+ case "metadata" {
+ # start comments here
+ print "/*\n";
+ $self->{comment} = 1;
+ }
+
+ # Print the var definition
+ case "var" {
+ $self->generate_var($element->{Attributes});
+ }
+
+ case "member" {
+ if ($self->{context}->[-2] eq "var") {
+ if ($self->{"list_hasmem"} eq 1) {
+ print ", ";
+ }
+ $self->{"list_hasmem"} = 1;
+ $self->{"list_num"} ++;
+ }
+ }
+
+
+ # The framework statements
+ case [@framework_statement] {
+ # Because the implementationAttribute & hasFeature belongs to both
+ # framework-statement and condition, we should distinct the two
+ # situation here. Let the generate_condtion to do the work.
+ if ($en eq "hasFeature" || $en eq "implementationAttribute") {
+ next;
+ }
+
+ $self->generate_framework_statement($en, $element->{Attributes});
+ }
+
+ case [@control] {
+ $self->generate_control_statement($en, $element->{Attributes});
+ }
+
+ # Test condition
+ case [@condition] {
+ $self->generate_condition($en, $element->{Attributes});
+ }
+
+ # The assertsions
+ case [@assertion] {
+ $self->generate_assertion($en, $element->{Attributes});
+ }
+
+ case "assertDOMException" {
+ # Indeed, nothing to do here!
+ }
+
+ # Deal with exception
+ case [@exception] {
+ # Just see end_element
+ $self->{'exception'} = 1;
+ }
+
+ # Then catch other case
+ else {
+ # we don't care the comment nodes
+ if ($self->{comment} eq 0) {
+ $self->generate_interface($en, $element->{Attributes});
+ }
+ }
+ }
+}
+
+sub end_element {
+ my ($self, $element) = @_;
+
+ my @ct = @{$self->{context}};
+ my $name = pop(@{$self->{context}});
+
+ switch ($name) {
+ case "metadata" {
+ print "*/\n";
+ $self->{comment} = 0;
+ $self->generate_main();
+ }
+ case "test" {
+ $self->cleanup();
+ }
+
+ case "var" {
+ $self->generate_list();
+ }
+
+ # End of condition
+ case [@condition] {
+ $self->complete_condition($name);
+ }
+
+ # The assertion
+ case [@assertion] {
+ $self->complete_assertion($name);
+ }
+
+ case [@control] {
+ $self->complete_control_statement($name);
+ }
+
+ case [@exception] {
+ $name = "DOM_".$name;
+ print "assert(exp == $exceptions{$name});\n";
+ $self->{'exception'} = 0;
+ }
+
+ }
+}
+
+sub characters {
+ my ($self, $data) = @_;
+ our $description;
+
+ my $ct = $self->{context};
+
+ if ($self->{"inline_comment"} eq 1) {
+ print "$data->{Data}";
+ return ;
+ }
+
+ # We print the comments here
+ if ($self->{comment} eq 1) {
+ # So, we are in comments state
+ my $top = $ct->[$#{$ct}];
+ if ($top eq "metadata") {
+ return;
+ }
+
+ if ($top eq "description") {
+ if ($description eq 0) {
+ print "description: \n";
+ $description = 1;
+ }
+ print "$data->{Data}";
+ } else {
+ print "$top: $data->{Data}\n";
+ }
+ return;
+ }
+
+ if ($self->{context}->[-1] eq "member") {
+ # We should mark that the List/Collection has members
+ $self->{"list_hasmem"} = 1;
+
+ # Here, we should detect the characters type
+ # whether it is a integer or string (now, we only take care
+ # of the two types, because I did not find any other type).
+ if ($self->{"list_type"} eq "") {
+ if ($data->{Data} =~ /^\"/) {
+ $self->{"list_type"} = "char *";
+ print "char *".$self->{"list_name"}."Array[] = \{ $data->{Data}";
+ } else {
+ if ($data->{Data} =~ /^[0-9]+/) {
+ $self->{"list_type"} = "int *";
+ print "int ".$self->{"list_name"}."Array[] = \{ $data->{Data}";
+ } else {
+ die "Some data in the <member> we can't process: \"$data->{Data}\"";
+ }
+ }
+ } else {
+ # So, we must have known the type, just output the member
+ print "$data->{Data}";
+ }
+ }
+}
+
+sub generate_main {
+ my $self = shift;
+ # Firstly, push a new "b" to the string_unref list
+ push(@{$self->{"string_unref"}}, "b");
+
+ print << "__EOF__"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include <dom/dom.h>
+#include <dom/functypes.h>
+
+#include <domts.h>
+
+dom_implementation *doc_impl;
+lwc_context *ctx;
+
+int main(int argc, char **argv)
+{
+ dom_exception exp;
+ lwc_error lerr;
+
+ /* Firstly, initialise dom and dom implementations */
+ exp = dom_initialise(myrealloc, NULL);
+ if (exp != DOM_NO_ERR)
+ return exp;
+
+ lerr = lwc_create_context(myrealloc, NULL, &ctx);
+ if (lerr != lwc_error_ok)
+ return -1;
+
+__EOF__
+}
+
+# Note that, we have not just declare variables here
+# we should also define EventListener here!
+# I think this should be done after the EventListener design
+# is complete
+sub generate_var {
+ my ($self, $ats) = @_;
+
+ my $type = "";
+ my $dstring = "";
+
+ # For the case like <var name="v" type="DOMString" value="some some"
+ if ($ats->{"type"} eq "DOMString" and exists $ats->{"value"}) {
+ $dstring = $self->generate_domstring($ats->{"value"});
+ $ats->{"value"} = $dstring;
+ }
+
+ $type = type_to_ctype($ats->{"type"});
+ if ($type eq "") {
+ print "Not implement this type now\n";
+ return;
+ }
+
+ print "\t$type$ats->{'name'}";
+ if (exists $ats->{"value"}) {
+ print " = $ats->{'value'};\n";
+ } else {
+ if ($type =~ m/\*/) {
+ print " = NULL;";
+ } else {
+ print ";\n";
+ }
+ }
+
+ my $var = $self->{"var"};
+ $var->{$ats->{"name"}} = $ats->{"type"};
+
+ # If the type is List/Collection, we should take care of it
+ if ($ats->{"type"} =~ /^(List|Collection)$/) {
+ $self->{"list_name"} = $ats->{"name"};
+ }
+}
+
+sub generate_list {
+ my $self = shift;
+
+ # We should deal with the end of <var> when the <var> is declaring a List/Collection
+ if ($self->{"list_hasmem"} eq 1) {
+ # Yes, we are in List/Collection declaration
+ # Firstly, enclose the Array declaration
+ print "};\n";
+
+ # Now, we should create the list * for the List/Collection
+ # Note, we should deal with "int" or "string" type with different params.
+ if ($self->{"list_type"} eq "char *") {
+ print $self->{"list_name"}." = list_new(STRING);\n";
+ }
+ if ($self->{"list_type"} eq "int *") {
+ print $self->{"list_name"}." = list_new(INT);\n";
+ }
+ if ($self->{"list_type"} eq "") {
+ die "A List/Collection has children member but no type is impossible!";
+ }
+ for (my $i = 0; $i < $self->{"list_num"}; $i++) {
+ # Use *(char **) to convert char *[] to char *
+ print "list_add(".$self->{"list_name"}.", *(char **)(".$self->{"list_name"}."Array + $i));\n";
+ }
+ } else {
+ if ($self->{"list_name"} ne "") {
+ #TODO: generally, we set the list type as dom_string, but it may be dom_node
+ print $self->{"list_name"}." = list_new(DOM_STRING);\n";
+ $self->{"list_type"} = "DOMString";
+ }
+ }
+
+ # Add the List/Collection to map
+ $self->{"list_map"}->{$self->{"list_name"}} = $self->{"list_type"};
+
+ # Reset the List/Collection member state
+ $self->{"list_hasmem"} = 0;
+ $self->{"list_name"} = "";
+ $self->{"list_type"} = "";
+ $self->{"list_num"} = 0;
+}
+
+sub generate_load {
+ my ($self, $a) = @_;
+ my %ats = %$a;
+ my $doc = $ats{"var"};
+
+ $test_index ++;
+ # define the test file path, use HTML if there is, otherwise using XML
+ # Attention: I intend to copy the test files to the program excuting dir
+ print "\tchar *test$test_index = \"$ats{'href'}.html\";\n\n";
+ print "$doc = load_html(test$test_index, $ats{'willBeModified'});";
+ print "if ($doc == NULL) {";
+ $test_index ++;
+ print " char *test$test_index = \"$ats{'href'}.xml\";\n\n";
+ print " $doc = load_xml(test$test_index, $ats{'willBeModified'});";
+ print " if ($doc == NULL)";
+ print " return 1;";
+ print "}";
+ print << "__EOF__";
+exp = dom_document_get_implementation($doc, &doc_impl);
+if (exp != DOM_NO_ERR)
+ return exp;
+__EOF__
+
+ $self->addto_cleanup($doc);
+}
+
+sub generate_framework_statement {
+ my ($self, $name, $ats) = @_;
+
+ switch($name) {
+ case "load" {
+ $self->generate_load($ats);
+ }
+
+ case "assign" {
+ my $var = $ats->{"var"};
+ my $value = "0";
+ if (exists $ats->{"value"}) {
+ $value = $ats->{"value"};
+ }
+
+ # Assign with strong-type-conversion, this is necessary in C.
+ # And we may need to do deep-copy in the future. FIXME
+ my $type = type_to_ctype($self->{"var"}->{$var});
+ print "$var = \($type\) $value;\n";
+ }
+
+ case "increment" {
+ my $var = $ats->{"var"};
+ my $value = $ats->{"value"};
+
+ print "$var += $value;\n";
+ }
+
+ case "decrement" {
+ my $var = $ats->{"var"};
+ my $value = $ats->{"value"};
+
+ print "$var -= $value;\n";
+ }
+
+ case "append" {
+ my $col = $ats->{"collection"};
+ my $obj = "";
+
+ # God, the DTD said, there should be a "OBJ" attribute, but there may not!
+ if (exists $ats->{"obj"}) {
+ $obj = $ats->{"obj"};
+ } else {
+ $obj = $ats->{"item"}
+ }
+
+ if (not $self->{"var"}->{$col} =~ /^(List|Collection)/) {
+ die "Append data to some non-list type!";
+ }
+
+ print "list_add($col, $obj);\n";
+ }
+
+ case [qw(plus subtract mult divide)] {
+ my $var = $ats->{"var"};
+ my $op1 = $ats->{"op1"};
+ my $op2 = $ats->{"op2"};
+
+ my %table = ("plus", "+", "subtract", "-", "mult", "*", "divide", "/");
+ print "$var = $op1 $table{$name} $op2;\n";
+ }
+
+ case "comment" {
+ print "\*";
+ $self->{"inline_comment"} = 1;
+ }
+
+ case "implementation" {
+ if (not exists $ats->{"obj"}) {
+ my $var = $ats->{"var"};
+ my $dstring = generate_domstring($self, $dom_feature);
+ print "exp = dom_implregistry_get_dom_implementation($dstring, \&$var);";
+ print "if (exp != DOM_NO_ERR) {";
+ $self->cleanup_fail("\t\t");
+ print "return exp;}";
+ last;
+ }
+
+ my $obj = $ats->{"obj"};
+ my $var = $ats->{"var"};
+ # Here we directly output the libDOM's get_implementation API
+ print "exp = dom_document_get_implementation($obj, \&$var);";
+ print "if (exp != DOM_NO_ERR) {";
+ $self->cleanup_fail("\t\t");
+ print "return exp;}";
+ }
+
+ # We deal with hasFeaturn and implementationAttribute in the generate_condition
+ case "hasFeature" {
+ die "No, never can be here!";
+ }
+ case "implementaionAttribute" {
+ die "No, never can be here!";
+ }
+
+ # Here, we die because we did not implement other statements
+ # We did not implement these statements, because there are no use of them in the W3C DOMTS now
+ case [@framework_statement] {
+ die "The statement \"$name\" is not implemented yet!";
+ }
+
+ }
+}
+
+sub complete_framework_statement {
+ my ($self, $name) = @_;
+
+ switch($name) {
+ case "comment" {
+ print "*/\n";
+ $self->{"inline_comment"} = 0;
+ }
+ }
+}
+
+sub generate_interface {
+ my ($self, $en, $a) = @_;
+ my %ats = %$a;
+ my $dd = $self->{dd};
+
+ if (exists $ats{'interface'}) {
+ # Firstly, test whether it is a DOM native interface
+ if (exists $native_interface{$ats{'interface'}}) {
+ if ($native_interface{$ats{'interface'}} eq "") {
+ die "Unkown how to deal with $en of $ats{'interface'}";
+ }
+
+ return $native_interface{$ats{'interface'}}($self, $en, $a);
+ }
+
+ my $ns = $dd->find("/library/interface[\@name=\"$ats{'interface'}\"]/method[\@name=\"$en\"]");
+ if ($ns->size() != 0) {
+ my $node = $ns->get_node(1);
+ $self->generate_method($en, $node, %ats);
+ } else {
+ my $ns = $dd->find("/library/interface[\@name=\"$ats{'interface'}\"]/attribute[\@name=\"$en\"]");
+ if ($ns->size() != 0) {
+ my $node = $ns->get_node(1);
+ $self->generate_attribute_accessor($en, $node, %ats);
+ }
+ }
+ } else {
+ my $ns = $dd->find("/library/interface/method[\@name=\"$en\"]");
+ if ($ns->size() != 0) {
+ my $node = $ns->get_node(1);
+ $self->generate_method($en, $node, %ats);
+ } else {
+ my $ns = $dd->find("/library/interface/attribute[\@name=\"$en\"]");
+ if ($ns->size() != 0) {
+ my $node = $ns->get_node(1);
+ $self->generate_attribute_accessor($en, $node, %ats);
+ } else {
+ die "Oh, Can't find how to deal with the element $en\n";
+ }
+ }
+ }
+}
+
+sub generate_method {
+ my ($self, $en, $node, %ats) = @_;
+ my $dd = $self->{dd};
+ if (! exists $ats{'interface'}) {
+ my $n = $node;
+ while($n->getLocalName() ne "interface") {
+ $n = $n->getParentNode();
+ }
+ $ats{'interface'} = $n->getAttribute("name");
+ }
+
+ $method = to_cmethod($ats{'interface'}, $en);
+
+ my $ns = $dd->find("parameters/param", $node);
+ my $params = "$ats{'obj'}";
+ for ($count = 1; $count <= $ns->size; $count++) {
+ my $n = $ns->get_node($count);
+ my $p = $n->getAttribute("name");
+ my $t = $n->getAttribute("type");
+
+ # Change the raw string and the char * to dom_string
+ if ($t eq "DOMString") {
+ if ($ats{$p} =~ /^"/ or $self->{"var"}->{$ats{$p}} eq "char *") {
+ $self->generate_domstring($ats{$p});
+ $params = $params.", dstring$string_index";
+ next;
+ }
+ }
+
+ # For the case that the testcase did not provide the param, we just pass a NULL
+ # Because we are in C, not like C++ which can overriden functions
+ if (not exists $ats{$p}) {
+ $params = $params.", NULL";
+ next;
+ }
+
+ $params = $params.", $ats{$p}";
+ }
+
+ #$ns = $dd->find("returns", $node);
+ #my $n = $ns->get_node(1);
+ #my $t = $n->getAttribute("type");
+ # declare the return value
+ #my $tp = type_to_ctype($t);
+ #print "\t$tp ret$ret_index;\n";
+ my $unref = 0;
+ my $temp_node = 0;
+ if (exists $ats{'var'}) {
+ # Add the bootstrap params
+ if (exists $bootstrap_api{$method}) {
+ $params = $params.", myrealloc, NULL, ctx";
+ }
+ # Deal with the situation like
+ #
+ # dom_node_append_child(node, new_node, &node);
+ #
+ # Here, we should import a tempNode, and change this expression to
+ #
+ # dom_node *tnode1 = NULL;
+ # dom_node_append_child(node, new_node, &tnode1);
+ # dom_node_unref(node);
+ # node = tnode1;
+ #
+ # Over.
+ if ($ats{'obj'} eq $ats{'var'}) {
+ my $t = type_to_ctype($self->{'var'}->{$ats{'var'}});
+ $tnode_index ++;
+ print "$t tnode$tnode_index = NULL;";
+ $params = $params.", \&tnode$tnode_index";
+ # The ats{'obj'} must have been added to cleanup stack
+ $unref = 1;
+ # Indicate that we have created a temp node
+ $temp_node = 1;
+ } else {
+ $params = $params.", (void *) \&$ats{'var'}";
+ $unref = $self->param_unref($ats{'var'});
+ }
+ }
+
+ print "exp = $method($params);";
+
+ if ($self->{'exception'} eq 0) {
+ print << "__EOF__";
+ if (exp != DOM_NO_ERR) {
+ fprintf(stderr, "Exception raised from %s\\n", "$method");
+__EOF__
+
+ $self->cleanup_fail("\t\t");
+ print << "__EOF__";
+ return exp;
+ }
+__EOF__
+ }
+
+ if (exists $ats{'var'} and $unref eq 0) {
+ $self->addto_cleanup($ats{'var'});
+ }
+
+ if ($temp_node eq 1) {
+ my $t = $self->{'var'}->{$ats{'var'}};
+ if (not exists $no_unref{$t}) {
+ my $prefix = "dom_node";
+ if (exists $unref_prefix{$t}) {
+ $prefix = $unref_prefix{$t};
+ }
+ print $prefix."_unref(".$ats{'obj'}.");\n";
+ }
+ print "$ats{'var'} = tnode$tnode_index;";
+ }
+}
+
+sub generate_attribute_accessor {
+ my ($self, $en, $node, %ats) = @_;
+
+ if (defined($ats{'var'})) {
+ generate_attribute_fetcher(@_);
+ } else {
+ if (defined($ats{'value'})) {
+ generate_attribute_setter(@_);
+ }
+ }
+}
+
+sub generate_attribute_fetcher {
+ my ($self, $en, $node, %ats) = @_;
+ my $dd = $self->{dd};
+ if (! exists $ats{'interface'}) {
+ my $n = $node;
+ while($n->getLocalName() ne "interface") {
+ $n = $n->getParentNode();
+ }
+ $ats{'interface'} = $n->getAttribute("name");
+ }
+
+ my $fetcher = to_attribute_fetcher($ats{'interface'}, "$en");
+
+ my $unref = 0;
+ my $temp_node = 0;
+ # Deal with the situation like
+ #
+ # dom_node_get_next_sibling(child, &child);
+ #
+ # Here, we should import a tempNode, and change this expression to
+ #
+ # dom_node *tnode1 = NULL;
+ # dom_node_get_next_sibling(child, &tnode1);
+ # dom_node_unref(child);
+ # child = tnode1;
+ #
+ # Over.
+ if ($ats{'obj'} eq $ats{'var'}) {
+ my $t = type_to_ctype($self->{'var'}->{$ats{'var'}});
+ $tnode_index ++;
+ print "$t tnode$tnode_index = NULL;";
+ print "exp = $fetcher($ats{'obj'}, \&tnode$tnode_index);";
+ # The ats{'obj'} must have been added to cleanup stack
+ $unref = 1;
+ # Indicate that we have created a temp node
+ $temp_node = 1;
+ } else {
+ $unref = $self->param_unref($ats{'var'});
+ print "exp = $fetcher($ats{'obj'}, \&$ats{'var'});";
+ }
+
+
+ if ($self->{'exception'} eq 0) {
+ print << "__EOF__";
+ if (exp != DOM_NO_ERR) {
+ fprintf(stderr, "Exception raised when fetch attribute %s", "$en");
+__EOF__
+ $self->cleanup_fail("\t\t");
+ print << "__EOF__";
+ return exp;
+ }
+__EOF__
+ }
+
+ if ($temp_node eq 1) {
+ my $t = $self->{'var'}->{$ats{'var'}};
+ if (not exists $no_unref{$t}) {
+ my $prefix = "dom_node";
+ if (exists $unref_prefix{$t}) {
+ $prefix = $unref_prefix{$t};
+ }
+ print $prefix."_unref(".$ats{'obj'}.");\n";
+ }
+ print "$ats{'var'} = tnode$tnode_index;";
+ }
+
+ if ($unref eq 0) {
+ $self->addto_cleanup($ats{'var'});
+ }
+}
+
+sub generate_attribute_setter {
+ my ($self, $en, $node, %ats) = @_;
+ my $dd = $self->{dd};
+ if (! exists $ats{'interface'}) {
+ my $n = $node;
+ while($n->getLocalName() ne "interface") {
+ $n = $n->getParentNode();
+ }
+ $ats{'interface'} = $n->getAttribute("name");
+ }
+
+ my $setter = to_attribute_setter($ats{'interface'}, "$en");
+ my $param = "$ats{'obj'}";
+
+ # For DOMString, we should deal specially
+ my $lp = $ats{'value'};
+ if ($node->getAttribute("type") eq "DOMString") {
+ if ($ats{'value'} =~ /^"/ or $self->{"var"}->{$ats{'value'}} eq "char *") {
+ $lp = $self->generate_domstring($ats{'value'});
+ }
+ }
+
+ $param = $param.", $lp";
+
+ print "exp = $setter($param);";
+
+ if ($self->{'exception'} eq 0) {
+ print << "__EOF__";
+ if (exp != DOM_NO_ERR) {
+ fprintf(stderr, "Exception raised when fetch attribute %s", "$en");
+__EOF__
+ $self->cleanup_fail("\t\t");
+ print << "__EOF__";
+ return exp;
+ }
+__EOF__
+ }
+
+}
+
+
+sub generate_condition {
+ my ($self, $name, $ats) = @_;
+
+ # If we are in nested or/and/xor/not, we should put a operator before test
+ my @array = @{$self->{condition_stack}};
+ if ($#array ge 0) {
+ switch ($array[-1]) {
+ case "xor" {
+ print " ^ ";
+ }
+ case "or" {
+ print " || ";
+ }
+ case "and" {
+ print " && ";
+ }
+ # It is the indicator, just pop it.
+ case "new" {
+ pop(@{$self->{condition_stack}});
+ }
+ }
+ }
+
+ switch ($name) {
+ case [qw(less lessOrEquals greater greaterOrEquals)] {
+ my $actual = $ats->{actual};
+ my $expected = $ats->{expected};
+ my $method = $name;
+ $method =~ s/[A-Z]/_$&/g;
+ $method = lc $method;
+ print "$method($expected, $actual)";
+ }
+
+ case "same" {
+ my $actual = $ats->{actual};
+ my $expected = $ats->{expected};
+ my $func = $self->find_override("is_same", $actual, $expected);
+ print "$func($expected, $actual)";
+ }
+
+ case [qw(equals notEquals)]{
+ my $actual = $ats->{actual};
+ my $expected = $ats->{expected};
+ my $ig;
+ if (exists $ats->{ignoreCase}){
+ $ig = $ats->{ignoreCase};
+ } else {
+ $ig = "false";
+ }
+ $ig = adjust_ignore($ig);
+
+ my $func = $self->find_override("is_equals", $actual, $expected);
+ if ($name =~ /not/i){
+ print "(false == $func($expected, $actual, $ig))";
+ } else {
+ print "$func($expected, $actual, $ig)";
+ }
+ }
+
+ case [qw(isNull notNull)]{
+ my $obj = $ats->{obj};
+ if ($name =~ /not/i) {
+ print "(false == is_null($obj))";
+ } else {
+ print "is_null($obj)";
+ }
+ }
+
+ case "isTrue" {
+ my $value = $ats->{value};
+ print "is_true($value)";
+ }
+
+ case "isFalse" {
+ my $value = $ats->{value};
+ print "(false == is_true($value))";
+ }
+
+ case "hasSize" {
+ my $obj = $ats->{obj};
+ my $size = $ats->{expected};
+ my $func = $self->find_override("is_size", $obj, $size);
+ print "$func($size, $obj)";
+ }
+
+ case "contentType" {
+ my $type = $ats->{type};
+ print "is_contenttype(\"$type\")";
+ }
+
+ case "instanceOf" {
+ my $obj = $ats->{obj};
+ my $type = $ats->{type};
+ print "instanceOf(\"$type\", $obj)";
+ }
+
+ case "hasFeature" {
+ if (exists $ats->{var}) {
+ $self->generate_interface($name, $ats);
+ } else {
+ my $feature = $ats->{feature};
+ if (not ($feature =~ /^"/)) {
+ $feature = '"'.$feature.'"';
+ }
+ my $version = "NULL";
+ if (exists $ats->{version}) {
+ $version = $ats->{version};
+ if (not ($version =~ /^"/)) {
+ $version = '"'.$version.'"';
+ }
+
+ }
+
+ if ($self->{context}->[-2] ne "condition") {
+ # we are not in a %condition place, so we must be a statement
+ # we change this to assert
+ # print "assert(has_feature($feature, $version));\n"
+ # do nothing if we are not in condition.
+ } else {
+ print "has_feature($feature, $version)";
+ }
+ }
+ }
+
+ case "implementationAttribute" {
+ my $value = $ats->{value};
+ my $name = $ats->{name};
+
+ if ($self->{context}->[-2] ne "condition") {
+ # print "assert(implementation_attribute(\"$name\", $value));";
+ # Do nothing, and the same with hasFeature, this means we will
+ # run all test cases now and try to get a result whether we support
+ # such feature.
+ } else {
+ print "implementation_attribute(\"$name\", $value)";
+ }
+ }
+
+ case [qw(and or xor)] {
+ push(@{$self->{condition_stack}}, $name);
+ push(@{$self->{condition_stack}}, "new");
+ print "(";
+ }
+
+ case "not" {
+ push(@{$self->{condition_stack}}, $name);
+ print "(false == ";
+ }
+ }
+
+}
+
+sub complete_condition {
+ my ($self, $name) = @_;
+
+ if ($name =~ /^(xor|or|and)$/i) {
+ print ")";
+ my $top = pop(@{$self->{condition_stack}});
+ die "Condition stack error! $top != $name" if $top ne $name;
+ }
+
+ if ($name eq "not") {
+ my $top = pop(@{$self->{condition_stack}});
+ die "Condition stack error! $top != $name" if $top ne $name;
+ print ")";
+ }
+
+ # we deal with the situation that the %condition is in a control statement such as
+ # <if> or <while>, and we should start a new '{' block here
+ if ($self->{context}->[-1] eq "condition") {
+ print ") {\n";
+ pop(@{$self->{context}});
+ }
+}
+
+sub generate_assertion {
+ my ($self, $name, $ats) = @_;
+
+ print "\tassert(";
+ switch($name){
+ # Only assertTrue & assertFalse can have nested %conditions
+ case [qw(assertTrue assertFalse assertNull)] {
+ my $n = $name;
+ $n =~ s/assert/is/g;
+ if (exists $ats->{actual}){
+ my $ta = { value => $ats->{actual}, obj => $ats->{actual}};
+ $self->generate_condition($n,$ta);
+ }
+ }
+
+ case [qw(assertNotNull assertEquals assertNotEquals assertSame)] {
+ my $n = $name;
+ $n =~ s/assert//g;
+ $n = lcfirst $n;
+ if (exists $ats->{actual}){
+ my $ta = {
+ actual => $ats->{actual},
+ value => $ats->{actual},
+ obj => $ats->{actual},
+ expected => $ats->{expected},
+ ignoreCase => $ats->{ignoreCase},
+ type => $ats->{type},
+ };
+ $self->generate_condition($n,$ta);
+ }
+ }
+
+ case "assertInstanceOf" {
+ my $obj = $ats->{obj};
+ my $type = $ats->{type};
+ print "is_instanceof(\"$type\", $obj)";
+ }
+
+ case "assertSize" {
+ my $n = $name;
+ $n =~ s/assert/has/;
+ if (exists $ats->{collection}){
+ my $ta = { obj => $ats->{collection}, expected => $ats->{size}};
+ $self->generate_condition($n,$ta);
+ }
+ }
+
+ case "assertEventCount" {
+ #todo
+ }
+
+ case "assertURIEquals" {
+ my $actual = $ats->{actual};
+ my ($scheme, $path, $host, $file, $query, $fragment, $isAbsolute) = qw(NULL NULL NULL NULL NULL NULL NULL);
+ if (exists $ats->{scheme}) {
+ $scheme = $ats->{scheme};
+ }
+ if (exists $ats->{path}) {
+ $path = $ats->{path};
+ }
+ if (exists $ats->{host}) {
+ $host = $ats->{host};
+ }
+ if (exists $ats->{file}) {
+ $file = $ats->{file};
+ }
+ if (exists $ats->{query}) {
+ $query = $ats->{query};
+ }
+ if (exists $ats->{fragment}) {
+ $fragment = $ats->{fragment};
+ }
+ if (exists $ats->{isAbsolute}) {
+ $isAbsolute = $ats->{isAbsolute};
+ }
+
+ print "is_uri_equals($scheme, $path, $host, $file, $query, $fragment, $isAbsolute, $actual)"
+ }
+ }
+
+}
+
+sub complete_assertion {
+ my ($self, $name) = @_;
+
+ print ");\n";
+}
+
+sub generate_control_statement {
+ my ($self, $name, $ats) = @_;
+
+ switch($name) {
+ case "if" {
+ print "\tif(";
+ push(@{$self->{"context"}}, "condition");
+ }
+
+ case "else" {
+ $self->cleanup_block_domstring();
+ print "\t} else {";
+ }
+
+ case "while" {
+ print "\twhile (";
+ push(@{$self->{"context"}}, "condition");
+ }
+
+ case "for-each" {
+ # Detect what is the collection type, if it is "string", we
+ # should also do some conversion work
+ my $coll = $ats->{"collection"};
+ # The default member type is dom_node
+ my $type = "dom_node *";
+ if (exists $self->{"list_map"}->{$coll}) {
+ $type = $self->{"list_map"}->{$coll};
+ }
+
+ # Find the member variable, if it is not declared before, declare it firstly
+ my $member = $ats->{"member"};
+ if (not exists $self->{"var"}->{$member}) {
+ print "$type $member;\n";
+ # Add the new variable to the {var} map
+ $self->{"var"}->{"$member"} = $type;
+ }
+
+ # Now the member is conformed to be declared
+ if ($self->{"var"}->{$coll} =~ /^(List|Collection)$/) {
+ # The element in the list is not equal with the member object
+ # For now, there is only one case for this, it is "char *" <=> "DOMString"
+ my $conversion = 0;
+ if ($self->{"var"}->{"$member"} ne $type) {
+ if ($self->{"var"}->{"$member"} eq "DOMString") {
+ if ($type eq "char *") {
+ $conversion = 1;
+ }
+ }
+ }
+
+ $iterator_index++;
+ print "unsigned int iterator$iterator_index = 0;";
+ if ($conversion eq 1) {
+ print "char *tstring$temp_index = NULL;";
+ }
+ print "foreach_initialise_list($coll, \&iterator$iterator_index);\n";
+ print "while(get_next_list($coll, \&iterator$iterator_index, ";
+ if ($conversion eq 1) {
+ print "\&tstring$temp_index)) {\n";
+ print "exp = dom_string_create(myrealloc, NULL, (const uint8_t *)tstring$temp_index,";
+ print "strlen(tstring$temp_index), &$member);";
+ print "if (exp != DOM_NO_ERR) {";
+ print "fprintf(stderr, \"Can't create DOMString\\n\");";
+ $self->cleanup_fail("\t\t");
+ print "return exp; }";
+ $temp_index ++;
+ } else {
+ print "\&$member)) {\n";
+ }
+ }
+
+ if ($self->{"var"}->{$coll} eq "NodeList") {
+ $iterator_index++;
+ print "unsigned int iterator$iterator_index = 0;";
+ print "foreach_initialise_domnodelist($coll, \&iterator$iterator_index);\n";
+ print "while(get_next_domnodelist($coll, \&iterator$iterator_index, \&$member)) {\n";
+ }
+
+ if ($self->{"var"}->{$coll} eq "NamedNodeMap") {
+ $iterator_index++;
+ print "unsigned int iterator$iterator_index = 0;";
+ print "foreach_initialise_domnamednodemap($coll, \&iterator$iterator_index);\n";
+ print "while(get_next_domnamednodemap($coll, \&iterator$iterator_index, \&$member)) {\n";
+ }
+ }
+ }
+
+ # Firstly, we enter a new block, so push a "b" into the string_unref list
+ push(@{$self->{"string_unref"}}, "b");
+}
+
+sub complete_control_statement {
+ my ($self, $name) = @_;
+
+ # Note: we only print a '}' when <if> element ended but not <else>
+ # The reason is that there may be no <else> element in <if> and
+ # we when there is an <else> element, it must nested in <if>. ^_^
+ switch($name) {
+ case [qw(if while for-each)] {
+ # Firstly, we should cleanup the dom_string in this block
+ $self->cleanup_block_domstring();
+
+ print "}\n";
+ }
+ }
+}
+
+
+###############################################################################
+#
+# The helper functions
+#
+sub generate_domstring {
+ my ($self, $str) = @_;
+ $string_index = $string_index + 1;
+
+ print << "__EOF__";
+ const char *string$string_index = $str;
+ dom_string *dstring$string_index;
+ exp = dom_string_create(myrealloc, NULL, (const uint8_t *)string$string_index,
+ strlen(string$string_index), &dstring$string_index);
+ if (exp != DOM_NO_ERR) {
+ fprintf(stderr, "Can't create DOMString\\n");
+__EOF__
+ $self->cleanup_fail("\t\t");
+ print << "__EOF__";
+ return exp;
+ }
+
+__EOF__
+
+ push(@{$self->{string_unref}}, "$string_index");
+
+ return "dstring$string_index";
+}
+
+sub cleanup_domstring {
+ my ($self, $indent) = @_;
+
+ for (my $i = 0; $i <= $#{$self->{string_unref}}; $i++) {
+ if ($self->{string_unref}->[$i] ne "b") {
+ print $indent."dom_string_unref(dstring$self->{string_unref}->[$i]);\n";
+ }
+ }
+}
+
+sub cleanup_block_domstring {
+ my $self = shift;
+
+ while ((my $num = pop(@{$self->{string_unref}})) ne "b" and $#{$self->{string_unref}} ne -1) {
+ print "dom_string_unref(dstring$num);\n";
+ }
+}
+
+sub type_to_ctype {
+ my $type = shift;
+
+ if (exists $special_type{$type}) {
+ return $special_type{$type};
+ }
+
+ # If the type is not specially treated, we can transform it by rules
+ if ($type =~ m/^HTML/) {
+ # Don't deal with this now
+ return "";
+ }
+
+ # The core module comes here
+ $type =~ s/[A-Z]/_$&/g;
+ $type = lc $type;
+ return "dom".$type." *";
+}
+
+sub to_cmethod {
+ my ($type, $m) = @_;
+ my $prefix = get_prefix($type);
+ my $ret;
+
+ if (exists $special_method{$m}) {
+ $ret = $prefix."_".$special_method{$m};
+ } else {
+ $m =~ s/[A-Z]/_$&/g;
+ $m = lc $m;
+ $ret = $prefix."_".$m;
+ }
+
+ $ret =~ s/h_t_m_l/html/;
+ $ret =~ s/c_d_a_t_a/cdata/;
+ $ret =~ s/_n_s$/_ns/;
+ return $ret;
+}
+
+sub to_attribute_fetcher {
+ return to_attribute_accessor(@_, "get");
+}
+
+sub to_attribute_setter {
+ return to_attribute_accessor(@_, "set");
+}
+
+sub to_attribute_accessor {
+ my ($type, $af, $accessor) = @_;
+ my $prefix = get_prefix($type);
+ my $ret;
+
+ if (exists $special_attribute{$af}) {
+ $ret = $prefix."_".$accessor."_".$special_attribute{$af};
+ } else {
+ $af =~ s/[A-Z]/_$&/g;
+ $af = lc $af;
+ $ret = $prefix."_".$accessor."_".$af;
+ }
+
+ $ret =~ s/h_t_m_l/html/;
+ return $ret;
+}
+
+sub get_prefix {
+ my $type = shift;
+
+ if (exists $special_prefix{$type}) {
+ $prefix = $special_prefix{$type};
+ } else {
+ $type =~ s/[A-Z]/_$&/g;
+ $prefix = lc $type;
+ $prefix = "dom".$prefix;
+ }
+ return $prefix;
+}
+
+# This function remain unsed
+sub get_suffix {
+ my $type = shift;
+ my $suffix = "default";
+
+ if (exists $override_suffix{$type}) {
+ $suffix = $override_suffix{$type};
+ } else {
+ $type =~ s/[A-Z]/_$&/g;
+ $suffix = lc $type;
+ $suffix = "dom".$suffix;
+ }
+ return $suffix;
+}
+
+#asserttions sometimes can contain sub-statements according the DTD. Like
+#<assertEquals ..>
+# <stat1 />
+# <stat2 />
+#</assertEquals>
+#
+# And assertion can contains assertions too! So, I use the assertion_stack
+# to deal:
+#
+# when we encounter an assertion, we push $assertionName, "end", "start" to
+# the stack, and when we encounter a statement, we examine the stack to see
+# the top element, if it is:
+#
+# 1. "start", then we are in sub-statement of that assertion, and this is the
+# the first sub-statement, so we should print a if (condtion==true) {, before
+# print the real statement.
+# 2. "end", then we are in sub-statement of that assertion, and we are not the
+# first one, just print the statement.
+#
+# But after searching the whole testcases, I found no use of sub-statements of assertions.
+# So, this function left unsed!
+
+sub end_half_assertion {
+ my ($self, $name) = @_;
+
+ my $top = pop(@{$self->{assertion_stack}});
+ if ($top eq "end") {
+ print "$self->{indent}"."}\n";
+ } else {
+ if ($top eq "start") {
+ pop(@{$self->{assertion_stack}});
+ pop(@{$self->{assertion_stack}});
+ }
+ }
+
+ pop(@{$self->{assertion_stack}});
+}
+### Enclose an unsed function
+##############################################################################################
+
+
+sub cleanup_domvar {
+ my ($self, $indent) = @_;
+
+ my $str = join($indent, @{$self->{unref}});
+ print $indent.$str."\n";
+}
+
+sub cleanup_fail {
+ my ($self, $indent) = @_;
+
+ $self->cleanup_domstring($indent);
+ $self->cleanup_domvar($indent);
+}
+
+sub cleanup {
+ my $self = shift;
+
+ print "\n\n";
+ $self->cleanup_domstring("\t");
+ $self->cleanup_domvar("\t");
+ print "\n\treturn 0;\n";
+ print "\n\}\n";
+}
+
+sub addto_cleanup {
+ my ($self, $var) = @_;
+
+ my $type = $self->{'var'}->{$var};
+ if (not exists $no_unref{$type}) {
+ my $prefix = "dom_node";
+ if (exists $unref_prefix{$type}) {
+ $prefix = $unref_prefix{$type};
+ }
+ push(@{$self->{unref}}, $prefix."_unref(".$var.");\n");
+ }
+}
+
+sub adjust_ignore {
+ my $ig = shift;
+
+ if ($ig eq "auto"){
+ return "true";
+ }
+ return $ig;
+}
+
+sub find_override {
+ my ($self, $func, $var, $expected) = @_;
+ my $vn = $self->{var}->{$var};
+
+ # Deal with string types
+ if ($expected eq "DOMString") {
+ return $func."_domstring";
+ } else {
+ if ($expected =~ /^\"/ or $self->{"var"}->{$expected} eq "char *") {
+ return $func."_string";
+ }
+ }
+
+ if (exists $override_suffix{$vn}) {
+ $func = $func."_".$override_suffix{$vn}
+ }
+ return $func;
+}
+
+sub param_unref {
+ my ($self, $var) = @_;
+
+ my $type = $self->{'var'}->{$var};
+ if (not exists $no_unref{$type}) {
+ my $prefix = "dom_node";
+ if (exists $unref_prefix{$type}) {
+ $prefix = $unref_prefix{$type};
+ }
+ print "if ($var != NULL) {";
+ print $prefix."_unref(".$var.");\n";
+ print "$var = NULL;";
+ print "}";
+ }
+
+ foreach my $item (@{$self->{unref}}) {
+ $item =~ m/.*\((.*)\).*/;
+ if ($var eq $1) {
+ return 1;
+ }
+ }
+
+ foreach my $item (@{$self->{string_unref}}) {
+ if ($var eq $item) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+sub generate_domstring_interface {
+ my ($self, $en, $a) = @_;
+
+ switch ($en) {
+ case "length" {
+ print "$a->{'var'} = dom_string_length($a->{'obj'});";
+ }
+
+ else {
+ die "Can't generate method/attribute $en for DOMString";
+ }
+ }
+}
+
+1;
Index: test/testutils
===================================================================
Index: test/testutils/comparators.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ test/testutils/comparators.h 2009-07-30 11:27:55.000000000 +0100
@@ -0,0 +1,23 @@
+/*
+ * This file is part of libdom test suite.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2007 James Shaw <jshaw(a)netsurf-browser.org>
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ */
+
+#ifndef comparators_h_
+#define comparators_h_
+
+/**
+ * A function pointer type for a comparator.
+ */
+typedef int (*comparator)(const void* a, const void* b);
+
+int int_comparator(const void* a, const void* b);
+
+int str_icmp(const void *a, const void *b);
+int str_icmp_r(const void *a, const void *b);
+int str_cmp(const void *a, const void *b);
+int str_cmp_r(const void *a, const void *b);
+#endif
Index: test/testutils/load.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ test/testutils/load.c 2009-07-30 11:27:55.000000000 +0100
@@ -0,0 +1,151 @@
+/*
+ * This file is part of libdom test suite.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <libwapcaplet/libwapcaplet.h>
+
+// For parsers
+#include <dom/dom.h>
+#include <xmlparser.h>
+#include <parser.h>
+#include <errors.h>
+
+#include "utils.h"
+
+extern lwc_context *ctx;
+
+/**
+ * Load the file as it is a XML file
+ *
+ * \param file The file path
+ * \param willBeModified Whether this file will be modified, not used
+ */
+dom_document *load_xml(char *file, bool willBeModified)
+{
+ dom_xml_parser *parser = NULL;
+ int handle;
+ int readed;
+ dom_xml_error error;
+ dom_document *ret;
+ char buffer[1024];
+
+ UNUSED(willBeModified);
+
+ parser = dom_xml_parser_create(NULL, NULL,
+ myrealloc, NULL, mymsg, NULL, ctx);
+ if (parser == NULL) {
+ fprintf(stderr, "Can't create XMLParser\n");
+ return NULL;
+ }
+
+ handle = open(file, O_RDONLY);
+ if (handle == -1) {
+ dom_xml_parser_destroy(parser);
+ fprintf(stderr, "Can't open test input file: %s\n", file);
+ return NULL;
+ }
+
+ readed = read(handle, buffer, 1024);
+ error = dom_xml_parser_parse_chunk(parser, buffer, readed);
+ if (error != DOM_XML_OK) {
+ dom_xml_parser_destroy(parser);
+ fprintf(stderr, "Parsing errors occur\n");
+ return NULL;
+ }
+
+ while(readed == 1024) {
+ readed = read(handle, buffer, 1024);
+ error = dom_xml_parser_parse_chunk(parser, buffer, readed);
+ if (error != DOM_XML_OK) {
+ dom_xml_parser_destroy(parser);
+ fprintf(stderr, "Parsing errors occur\n");
+ return NULL;
+ }
+ }
+
+ error = dom_xml_parser_completed(parser);
+ if (error != DOM_XML_OK) {
+ dom_xml_parser_destroy(parser);
+ fprintf(stderr, "Parsing error when construct DOM\n");
+ return NULL;
+ }
+
+ ret = dom_xml_parser_get_document(parser);
+ dom_xml_parser_destroy(parser);
+
+ return ret;
+}
+
+/**
+ * Load the file as it is a HTML file
+ *
+ * \param file The file path
+ * \param willBeModified Whether this file will be modified, not used
+ */
+dom_document *load_html(char *file, bool willBeModified)
+{
+ dom_hubbub_parser *parser = NULL;
+ int handle;
+ int readed;
+ dom_hubbub_error error;
+ dom_document *ret;
+ char buffer[1024];
+
+ UNUSED(willBeModified);
+
+ parser = dom_hubbub_parser_create("data/Aliases", NULL, true,
+ myrealloc, NULL, mymsg, NULL, ctx);
+ if (parser == NULL) {
+ fprintf(stderr, "Can't create XMLParser\n");
+ return NULL;
+ }
+
+ handle = open(file, O_RDONLY);
+ if (handle == -1) {
+ dom_hubbub_parser_destroy(parser);
+ fprintf(stderr, "Can't open test input file: %s\n", file);
+ return NULL;
+ }
+
+ readed = read(handle, buffer, 1024);
+ error = dom_hubbub_parser_parse_chunk(parser, buffer, readed);
+ if (error != DOM_HUBBUB_OK) {
+ dom_hubbub_parser_destroy(parser);
+ fprintf(stderr, "Parsing errors occur\n");
+ return NULL;
+ }
+
+ while(readed == 1024) {
+ readed = read(handle, buffer, 1024);
+ error = dom_hubbub_parser_parse_chunk(parser, buffer, readed);
+ if (error != DOM_HUBBUB_OK) {
+ dom_hubbub_parser_destroy(parser);
+ fprintf(stderr, "Parsing errors occur\n");
+ return NULL;
+ }
+ }
+
+ error = dom_hubbub_parser_completed(parser);
+ if (error != DOM_HUBBUB_OK) {
+ dom_hubbub_parser_destroy(parser);
+ fprintf(stderr, "Parsing error when construct DOM\n");
+ return NULL;
+ }
+
+ ret = dom_hubbub_parser_get_document(parser);
+ dom_hubbub_parser_destroy(parser);
+
+ return ret;
+}
Index: test/testutils/domtsasserts.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ test/testutils/domtsasserts.h 2009-07-30 11:27:55.000000000 +0100
@@ -0,0 +1,61 @@
+/*
+ * This file is part of libdom test suite.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ */
+
+#ifndef domtsasserts_h_
+#define domtsasserts_h_
+
+#include <stdbool.h>
+
+#include <dom/dom.h>
+
+#include "list.h"
+
+
+/* Redefine assert, so we can simply use the standard assert mechanism
+ * within testcases and exit with the right output for the testrunner
+ * to do the right thing. */
+void __assert2(const char *expr, const char *function,
+ const char *file, int line);
+
+#define assert(expr) \
+ ((void) ((expr) || (__assert2 (#expr, __func__, __FILE__, __LINE__), 0)))
+
+bool is_true(bool arg);
+
+bool is_null(void *arg);
+
+bool is_same(void *excepted, void *actual);
+bool is_same_int(int excepted, int actual);
+bool is_same_unsigned_long(unsigned long excepted, unsigned long actual);
+
+bool is_equals_int(int excepted, int actual, bool dummy);
+bool is_equals_unsigned_long(unsigned long excepted, unsigned long actual, bool dummy);
+bool is_equals_string(const char *excepted, dom_string *actual,
+ bool ignoreCase);
+bool is_equals_domstring(dom_string *excepted, dom_string *actual, bool ignoreCase);
+
+/* We may use two different string types in libDOM, but the expected string type is
+ always "char *" */
+bool is_equals_list(list *expected, list *actual, bool ignoreCase);
+
+bool is_instanceof(const char *type, dom_node *node);
+
+bool is_size_domnamednodemap(int size, dom_namednodemap *map);
+bool is_size_domnodelist(int size, dom_nodelist *list);
+bool is_size_list(int size, list *list);
+
+bool is_uri_equals(char *scheme, char *path, char *host,
+ char *file, char *query, char *fragment,
+ bool isAbsolute, dom_string *actual);
+
+bool is_contenttype(const char *type);
+
+bool has_feature(char *feature, char *version);
+
+bool implementation_attribute(char *name, bool value);
+
+#endif
Index: test/testutils/list.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ test/testutils/list.h 2009-07-30 11:27:55.000000000 +0100
@@ -0,0 +1,70 @@
+/*
+ * This file is part of libdom test suite.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2007 James Shaw <jshaw(a)netsurf-browser.org>
+ */
+
+#ifndef list_h_
+#define list_h_
+
+#include <stdbool.h>
+
+#include "comparators.h"
+
+/* The element type in the list
+ *
+ * The high byte is used for category type
+ * The low byte is used for concrete type
+ */
+typedef enum TYPE {
+ INT = 0x0001,
+ STRING = 0x0100,
+ DOM_STRING = 0x0101,
+ NODE = 0x0200
+} TYPE;
+
+
+struct list_elt {
+ void* data;
+ struct list_elt* next;
+};
+
+typedef struct list {
+ unsigned int size;
+ TYPE type;
+ struct list_elt* head;
+ struct list_elt* tail;
+} list;
+
+struct list* list_new(TYPE type);
+void list_destroy(struct list* list);
+
+/**
+ * Add data to the tail of the list.
+ */
+void list_add(struct list* list, void* data);
+
+/**
+ * Remove element containing data from list.
+ * The list element is freed, but the caller must free the data itself
+ * if necessary.
+ *
+ * Returns true if data was found in the list.
+ */
+bool list_remove(struct list* list, void* data);
+
+struct list* list_clone(struct list* list);
+/**
+ * Tests if data is equal to any element in the list.
+ */
+bool list_contains(struct list* list, void* data,
+ comparator comparator);
+
+/**
+ * Tests if superlist contains all elements in sublist. Order is not important.
+ */
+bool list_contains_all(struct list* superList, struct list* subList,
+ comparator comparator);
+
+#endif
Index: test/testutils/utils.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ test/testutils/utils.c 2009-07-30 11:27:55.000000000 +0100
@@ -0,0 +1,34 @@
+/*
+ * This file is part of libdom test suite.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2007 John-Mark Bell <jmb(a)netsurf-browser.org>
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "utils.h"
+
+void *myrealloc(void *ptr, size_t len, void *pw)
+{
+ UNUSED(pw);
+
+ return realloc(ptr, len);
+}
+
+void mymsg(uint32_t severity, void *ctx, const char *msg, ...)
+{
+ va_list l;
+
+ UNUSED(ctx);
+
+ va_start(l, msg);
+
+ fprintf(stderr, "%d: ", severity);
+ vfprintf(stderr, msg, l);
+ fprintf(stderr, "\n");
+}
+
+
Index: test/testutils/domtscondition.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ test/testutils/domtscondition.h 2009-07-30 11:27:55.000000000 +0100
@@ -0,0 +1,18 @@
+/*
+ * This file is part of libdom test suite.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ */
+
+#ifndef domtscondition_h_
+#define domtscondition_h_
+
+#include <stdbool.h>
+
+inline bool less(int excepted, int actual);
+inline bool less_or_equals(int excepted, int actual);
+inline bool greater(int excepted, int actual);
+inline bool greater_or_equals(int excepted, int actual);
+
+#endif
Index: test/testutils/utils.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ test/testutils/utils.h 2009-07-30 11:27:55.000000000 +0100
@@ -0,0 +1,35 @@
+/*
+ * This file is part of libdom test suite.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2007 John-Mark Bell <jmb(a)netsurf-browser.org>
+ */
+
+#ifndef utils_h_
+#define utils_h_
+
+#include <stddef.h>
+#include <inttypes.h>
+
+#ifndef max
+#define max(a,b) ((a)>(b)?(a):(b))
+#endif
+
+#ifndef min
+#define min(a,b) ((a)<(b)?(a):(b))
+#endif
+
+#ifndef SLEN
+/* Calculate length of a string constant */
+#define SLEN(s) (sizeof((s)) - 1) /* -1 for '\0' */
+#endif
+
+#ifndef UNUSED
+#define UNUSED(x) ((x) = (x))
+#endif
+
+void *myrealloc(void *ptr, size_t len, void *pw);
+void mymsg(uint32_t severity, void *ctx, const char *msg, ...);
+
+#endif
+
Index: test/testutils/foreach.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ test/testutils/foreach.c 2009-07-30 11:27:55.000000000 +0100
@@ -0,0 +1,96 @@
+/*
+ * This file is part of libdom test suite.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ */
+
+#include <stdbool.h>
+
+#include "foreach.h"
+#include "list.h"
+
+#include <dom/dom.h>
+
+/**
+ * Please see foreach.h for the usage of the following functions
+ */
+
+void foreach_initialise_domnodelist(dom_nodelist *list, unsigned int *iterator)
+{
+ *iterator = 0;
+}
+
+void foreach_initialise_list(list *list, unsigned int *iterator)
+{
+ *iterator = 0;
+}
+
+void foreach_initialise_domnamednodemap(dom_namednodemap *map, unsigned int *iterator)
+{
+ *iterator = 0;
+}
+
+
+bool _get_next_domnodelist(dom_nodelist *list, unsigned int *iterator, dom_node **ret)
+{
+ dom_exception err;
+ unsigned long len;
+
+ err = dom_nodelist_get_length(list, &len);
+ if (err != DOM_NO_ERR)
+ return false;
+
+ if (*iterator >= len)
+ return false;
+
+ err = dom_nodelist_item(list, (*iterator), ret);
+ if (err != DOM_NO_ERR)
+ return false;
+
+ (*iterator)++;
+ return true;
+}
+
+bool get_next_list(list *list, unsigned int *iterator, void **ret)
+{
+ unsigned int len = *iterator;
+ unsigned int i = 0;
+ struct list_elt *elt = list->head;
+
+ for (; i < len; i++) {
+ if (elt == NULL)
+ return false;
+ elt = elt->next;
+ }
+
+ if (elt == NULL)
+ return false;
+
+ *ret = elt->data;
+
+ (*iterator)++;
+
+ return true;
+}
+
+bool _get_next_domnamednodemap(dom_namednodemap *map, unsigned int *iterator, dom_node **ret)
+{
+ dom_exception err;
+ unsigned long len;
+
+ err = dom_namednodemap_get_length(map, &len);
+ if (err != DOM_NO_ERR)
+ return false;
+
+ if (*iterator >= len)
+ return false;
+
+ err = dom_namednodemap_item(map, (*iterator), ret);
+ if (err != DOM_NO_ERR)
+ return false;
+
+ (*iterator)++;
+
+ return true;
+}
Index: test/testutils/comparators.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ test/testutils/comparators.c 2009-07-30 11:27:55.000000000 +0100
@@ -0,0 +1,80 @@
+/*
+ * This file is part of libdom test suite.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ */
+
+#include "comparators.h"
+#include "domts.h"
+
+#include <string.h>
+
+#include <dom/dom.h>
+
+/* Compare to integer, return zero if equal */
+int int_comparator(const void* a, const void* b) {
+ return *((const int *)a) - *((const int *)b);
+}
+
+/* Compare two string. The first one is a char * and the second
+ * one is a dom_string, return zero if equal */
+int str_cmp(const void *a, const void *b)
+{
+ const char *excepted = (const char *) a;
+ dom_string *actual = (dom_string *) b;
+ dom_string *exp;
+ dom_exception err;
+ bool ret;
+
+ err = dom_string_create(myrealloc, NULL, excepted, strlen(excepted),
+ &exp);
+ if (err != DOM_NO_ERR)
+ return false;
+
+ ret = dom_string_cmp(exp, actual) == 0;
+
+ dom_string_unref(exp);
+
+ if (ret == true)
+ return 0;
+ else
+ return 1;
+}
+
+/* Similar with str_cmp but the first param is a dom_string the second
+ * param is a char * */
+int str_cmp_r(const void *a, const void *b)
+{
+ return str_cmp(b, a);
+}
+
+/* Similar with str_cmp but ignore the case of letters */
+int str_icmp(const void *a, const void *b)
+{
+ const char *excepted = (const char *) a;
+ dom_string *actual = (dom_string *) b;
+ dom_string *exp;
+ dom_exception err;
+ bool ret;
+
+ err = dom_string_create(myrealloc, NULL, excepted, strlen(excepted),
+ &exp);
+ if (err != DOM_NO_ERR)
+ return false;
+
+ ret = dom_string_icmp(exp, actual) == 0;
+
+ dom_string_unref(exp);
+
+ if (ret == true)
+ return 0;
+ else
+ return 1;
+}
+
+/* Similar with str_icmp, but the param order are reverse */
+int str_icmp_r(const void *a, const void *b)
+{
+ return str_icmp(b, a);
+}
Index: test/testutils/domts.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ test/testutils/domts.h 2009-07-30 11:27:55.000000000 +0100
@@ -0,0 +1,25 @@
+/*
+ * This file is part of libdom test suite.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ */
+
+#ifndef domts_h_
+#define domts_h_
+
+#include <domtscondition.h>
+#include <domtsasserts.h>
+#include <list.h>
+#include <foreach.h>
+#include <utils.h>
+#include <comparators.h>
+
+struct lwc_context_s;
+
+extern struct lwc_context_s *ctx;
+
+dom_document *load_xml(char *file, bool willBeModified);
+dom_document *load_html(char *file, bool willBeModified);
+
+#endif
Index: test/testutils/domtsasserts.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ test/testutils/domtsasserts.c 2009-07-30 11:27:55.000000000 +0100
@@ -0,0 +1,253 @@
+/*
+ * This file is part of libdom test suite.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <dom/dom.h>
+
+#include "domts.h"
+
+extern dom_implementation *doc_impl;
+
+void __assert2(const char *expr, const char *function,
+ const char *file, int line)
+{
+ UNUSED(function);
+ UNUSED(file);
+
+ printf("FAIL - %s at line %d\n", expr, line);
+
+ exit(EXIT_FAILURE);
+}
+
+/**
+ * Following are the test conditions which defined in the DOMTS, please refer
+ * the DOM Test Suite for details
+ */
+
+bool is_true(bool arg)
+{
+ return arg == true;
+}
+
+bool is_null(void *arg)
+{
+ return arg == NULL;
+}
+
+bool is_same(void *excepted, void *actual)
+{
+ return excepted == actual;
+}
+
+bool is_same_int(int excepted, int actual)
+{
+ return excepted == actual;
+}
+
+bool is_same_unsigned_long(unsigned long excepted, unsigned long actual)
+{
+ return excepted == actual;
+}
+
+bool is_equals_int(int excepted, int actual, bool dummy)
+{
+ UNUSED(dummy);
+
+ return excepted == actual;
+}
+
+bool is_equals_unsigned_long(unsigned long excepted, unsigned long actual, bool dummy)
+{
+ UNUSED(dummy);
+
+ return excepted == actual;
+}
+
+/**
+ * Test whether two string are equal
+ *
+ * \param excepted The excepted string
+ * \param actual The actual string
+ * \param ignoreCase Whether to ignore letter case
+ */
+bool is_equals_string(const char *excepted, dom_string *actual,
+ bool ignoreCase)
+{
+ dom_string *exp;
+ dom_exception err;
+ bool ret;
+
+ err = dom_string_create(myrealloc, NULL, excepted, strlen(excepted),
+ &exp);
+ if (err != DOM_NO_ERR)
+ return false;
+
+ if (ignoreCase == true)
+ ret = dom_string_icmp(exp, actual) == 0;
+ else
+ ret = dom_string_cmp(exp, actual) == 0;
+
+ dom_string_unref(exp);
+ return ret;
+}
+
+/* Compare whether two dom_string are equal */
+bool is_equals_domstring(dom_string *excepted, dom_string *actual,
+ bool ignoreCase)
+{
+ if (ignoreCase == true)
+ return dom_string_icmp(excepted, actual) == 0;
+ else
+ return dom_string_cmp(excepted, actual) == 0;
+}
+
+/* The param actual should always contain dom_sting and expectd should
+ * contain char * */
+bool is_equals_list(list *expected, list *actual, bool ignoreCase)
+{
+ assert((expected->type && 0xff00) == (actual->type && 0xff00));
+
+ comparator cmp = NULL;
+ comparator rcmp = NULL;
+
+ if (expected->type == INT)
+ cmp = int_comparator;
+ if (expected->type == STRING) {
+ if (actual->type == DOM_STRING) {
+ cmp = ignoreCase? str_icmp : str_cmp;
+ rcmp = ignoreCase? str_icmp_r : str_cmp_r;
+ }
+ }
+ if (expected->type == DOM_STRING) {
+ if (actual->type == STRING) {
+ cmp = ignoreCase? str_icmp_r : str_cmp_r;
+ rcmp = ignoreCase? str_icmp : str_cmp;
+ }
+ }
+
+ assert(cmp != NULL);
+
+ bool ret = list_contains_all(expected, actual, cmp);
+ if (ret)
+ return list_contains_all(actual, expected, rcmp);
+}
+
+
+
+bool is_instanceof(const char *type, dom_node *node)
+{
+ assert("There is no instanceOf in the test-suite" == NULL);
+
+ return false;
+}
+
+
+bool is_size_domnamednodemap(int size, dom_namednodemap *map)
+{
+ unsigned long len;
+ dom_exception err;
+
+ err = dom_namednodemap_get_length(map, &len);
+ if (err != DOM_NO_ERR) {
+ assert("Exception occured" == NULL);
+ return false;
+ }
+
+ return size == len;
+}
+
+bool is_size_domnodelist(int size, dom_nodelist *list)
+{
+ unsigned long len;
+ dom_exception err;
+
+ err = dom_nodelist_get_length(list, &len);
+ if (err != DOM_NO_ERR) {
+ assert("Exception occured" == NULL);
+ return false;
+ }
+
+ return size == len;
+}
+
+bool is_size_list(int size, list *list)
+{
+ return size == list->size;
+}
+
+
+bool is_uri_equals(char *scheme, char *path, char *host,
+ char *file, char *query, char *fragment,
+ bool isAbsolute, dom_string *actual)
+{
+ UNUSED(scheme);
+ UNUSED(path);
+ UNUSED(host);
+ UNUSED(file);
+ UNUSED(query);
+ UNUSED(fragment);
+ UNUSED(isAbsolute);
+ UNUSED(actual);
+
+ return false;
+}
+
+
+bool is_contenttype(const char *type)
+{
+ /* Now, we use the libxml2 parser for DOM parsing, so the content type
+ * is always "text/xml" */
+ if (strcmp(type, "text/xml") == 0)
+ return true;
+ else
+ return false;
+}
+
+bool has_feature(char *feature, char *version)
+{
+ dom_exception err;
+ bool ret;
+ dom_string *df, *dv;
+
+ err = dom_string_create(myrealloc, NULL, feature,
+ feature == NULL ? 0 : strlen(feature), &df);
+ if (err != DOM_NO_ERR)
+ return false;
+
+ err = dom_string_create(myrealloc, NULL, version,
+ version == NULL ? 0 : strlen(version), &dv);
+ if (err != DOM_NO_ERR) {
+ dom_string_unref(df);
+ return false;
+ }
+
+ err = dom_implementation_has_feature(doc_impl, df, dv, &ret);
+ /* Here, when we come with exception, we should return false,
+ * TODO: this need to be improved, but I can't figure out how */
+ if (err != DOM_NO_ERR) {
+ dom_string_unref(df);
+ dom_string_unref(dv);
+ return false;
+ }
+
+ dom_string_unref(df);
+ dom_string_unref(dv);
+ return ret;
+}
+
+bool implementation_attribute(char *name, bool value)
+{
+ /* We didnot support DOMConfigure for implementation now */
+ UNUSED(name);
+ UNUSED(value);
+
+ return true;
+}
Index: test/testutils/list.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ test/testutils/list.c 2009-07-30 11:27:55.000000000 +0100
@@ -0,0 +1,171 @@
+/*
+ * This file is part of libdom test suite.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2007 James Shaw <jshaw(a)netsurf-browser.org>
+ * Copyright 2009 Bo Yang <struggeleyb.nku(a)gmail.com>
+ */
+
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <dom/core/string.h>
+#include <dom/core/node.h>
+
+#include "comparators.h"
+#include "list.h"
+#include "domtsasserts.h"
+
+/**
+ * Private helper function.
+ * Create a new list_elt and initialise it.
+ */
+struct list_elt* list_new_elt(void* data);
+
+struct list_elt* list_new_elt(void* data) {
+ struct list_elt* elt = malloc(sizeof(struct list_elt));
+ assert(elt != NULL);
+ elt->data = data;
+ elt->next = NULL;
+ return elt;
+}
+
+struct list* list_new(TYPE type)
+{
+ struct list* list = malloc(sizeof(struct list));
+ assert(list != NULL);
+ list->size = 0;
+ list->type = type;
+ list->head = NULL;
+ list->tail = NULL;
+ return list;
+}
+
+void list_destroy(struct list* list)
+{
+ struct list_elt* elt = list->head;
+ while (elt != NULL) {
+ if (list->type == DOM_STRING)
+ dom_string_unref((dom_string *) elt->data);
+ if (list->type == NODE)
+ dom_node_unref(elt->data);
+ struct list_elt* nextElt = elt->next;
+ free(elt);
+ elt = nextElt;
+ }
+ free(list);
+}
+
+void list_add(struct list* list, void* data)
+{
+ struct list_elt* elt = list_new_elt(data);
+ struct list_elt* tail = list->tail;
+
+ /* if tail was set, make its 'next' ptr point to elt */
+ if (tail != NULL) {
+ tail->next = elt;
+ }
+
+ /* make elt the new tail */
+ list->tail = elt;
+
+ if (list->head == NULL) {
+ list->head = elt;
+ }
+
+ /* inc the size of the list */
+ list->size++;
+ if (list->type == DOM_STRING)
+ dom_string_ref((dom_string *) data);
+ if (list->type == NODE)
+ dom_node_ref(data);
+}
+
+bool list_remove(struct list* list, void* data)
+{
+ struct list_elt* prevElt = NULL;
+ struct list_elt* elt = list->head;
+
+ bool found = false;
+
+ while (elt != NULL) {
+ struct list_elt* nextElt = elt->next;
+
+ /* if data is identical, fix up pointers, and free the element */
+ if (data == elt->data) {
+ if (prevElt == NULL) {
+ list->head = nextElt;
+ } else {
+ prevElt->next = nextElt;
+ }
+ free(elt);
+ list->size--;
+ found = true;
+ break;
+ }
+
+ prevElt = elt;
+ elt = nextElt;
+ }
+
+ return found;
+}
+
+struct list* list_clone(struct list* list)
+{
+ struct list* newList = list_new(list->type);
+ struct list_elt* elt = list->head;
+
+ while (elt != NULL) {
+ list_add(newList, elt->data);
+ elt = elt->next;
+ }
+
+ return newList;
+}
+
+bool list_contains(struct list* list, void* data, comparator comparator)
+{
+ struct list_elt* elt = list->head;
+ while (elt != NULL) {
+ if (comparator(elt->data, data) == 0) {
+ return true;
+ }
+ elt = elt->next;
+ }
+ return false;
+}
+
+bool list_contains_all(struct list* superList, struct list* subList,
+ comparator comparator)
+{
+ struct list_elt* subElt = subList->head;
+ struct list* superListClone = list_clone(superList);
+
+ bool found = true;
+
+ while (subElt != NULL) {
+ struct list_elt* superElt = superListClone->head;
+
+ found = false;
+ while (superElt != NULL && found == false) {
+ if (comparator(superElt->data, subElt->data) == 0) {
+ found = true;
+ list_remove(superListClone, superElt->data);
+ break;
+ }
+ superElt = superElt->next;
+ }
+
+ if (found == false)
+ break;
+ subElt = subElt->next;
+ }
+
+ free(superListClone);
+
+ return found;
+}
+
Index: test/testutils/foreach.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ test/testutils/foreach.h 2009-07-30 11:27:55.000000000 +0100
@@ -0,0 +1,41 @@
+/*
+ * This file is part of libdom test suite.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ */
+
+#ifndef domts_foreach_h_
+#define domts_foreach_h_
+
+#include <dom/dom.h>
+
+#include <list.h>
+
+/* The following six functions are used for the XML testcase's
+ <for-each> element.
+ And the <for-each> element can be converted to :
+
+ unsigned int iterator;
+ foreach_initialise_*(list, &iterator);
+ while(get_next_*(list, &iterator, ret)){
+ do the loop work.
+ }
+*/
+
+void foreach_initialise_domnodelist(dom_nodelist *list, unsigned int *iterator);
+void foreach_initialise_list(list *list, unsigned int *iterator);
+void foreach_initialise_domnamednodemap(dom_namednodemap *map, unsigned int *iterator);
+
+bool _get_next_domnodelist(dom_nodelist *list, unsigned int *iterator, dom_node **ret);
+#define get_next_domnodelist(l, i, r) _get_next_domnodelist( \
+ (dom_nodelist *) (l), (unsigned int *) (i), (dom_node **) (r))
+
+bool get_next_list(list *list, unsigned int *iterator, void **ret);
+
+bool _get_next_domnamednodemap(dom_namednodemap *map, unsigned int *iterator, dom_node **ret);
+#define get_next_domnamednodemap(m, i, r) _get_next_domnamednodemap( \
+ (dom_namednodemap *) (m), (unsigned int *) (i), (dom_node **) (r))
+
+
+#endif
Index: test/testutils/domtscondition.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ test/testutils/domtscondition.c 2009-07-30 11:27:55.000000000 +0100
@@ -0,0 +1,32 @@
+/*
+ * This file is part of libdom test suite.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ */
+
+#include <stdbool.h>
+
+/**
+ * Just simple functions which meet the needs of DOMTS conditions
+ */
+
+bool less(int excepted, int actual)
+{
+ return actual < excepted;
+}
+
+bool less_or_equals(int excepted, int actual)
+{
+ return actual <= excepted;
+}
+
+bool greater(int excepted, int actual)
+{
+ return actual > excepted;
+}
+
+bool greater_or_equals(int excepted, int actual)
+{
+ return actual >= excepted;
+}
Index: test/leak-test.sh
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ test/leak-test.sh 2009-07-30 11:27:55.000000000 +0100
@@ -0,0 +1,45 @@
+#!/bin/bash
+#
+# This is a simple script used to test libdom memory leakage.
+# Usage:
+# You should firstly run "run-test.sh", which will genrate a test output directory. In that
+# directory, there are C source files and corresponding executables.
+# Go to the test output directory. For example , for core, level 1, it is output/level1/core
+# And run this script as ../../../leak-test.sh "log-file"
+#
+# This file is part of libdom test suite.
+# Licensed under the MIT License,
+# http://www.opensource.org/licenses/mit-license.php
+# Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+
+log=$1;
+totla=0;
+leak=0;
+ok=0;
+while read f; do
+ #Test defnitely lost
+ echo -n "$f: " >&3;
+ echo -n "$f: ";
+ dl=$(valgrind "$f" 2>&1 | grep "definitely lost" | sed -e 's/definitely lost//g' -e 's/bytes in//g' -e 's/blocks.//g' -e 's/^.*://g' -e 's/ //g' -e 's/,//g');
+ pl=$(valgrind "$f" 2>&1 | grep "possibly lost" | sed -e 's/possibly lost//g' -e 's/bytes in//g' -e 's/blocks.//g' -e 's/^.*://g' -e 's/ //g' -e 's/,//g');
+
+ total=$((total+1));
+ if [ "$dl" -eq "00" -a "$pl" -eq "00" ]; then
+ echo "ok..." >&3;
+ echo "ok...";
+ ok=$((ok+1));
+ else
+ echo "leaked!" >&3;
+ echo "leaked!";
+ leak=$((leak+1));
+ fi
+
+done 3>"$log" < <(find ./ -perm -o=x -type f -print);
+
+echo "Total: $total" >>"$log";
+echo "Leak: $leak" >>"$log";
+echo "Ok: $ok" >>"$log";
+
+echo "Total: $total";
+echo "Leak: $leak";
+echo "Ok: $ok";
Index: test/data/Aliases
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ test/data/Aliases 2009-07-30 11:27:55.000000000 +0100
@@ -0,0 +1,303 @@
+# > Unicode:Files.Aliases
+# Mapping of character set encoding names to their canonical form
+#
+# Lines starting with a '#' are comments, blank lines are ignored.
+#
+# Based on http://www.iana.org/assignments/character-sets and
+# http://www.iana.org/assignments/ianacharset-mib
+#
+# Canonical Form MIBenum Aliases...
+#
+US-ASCII 3 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII ISO646-US ANSI_X3.4-1968 us IBM367 cp367 csASCII
+ISO-10646-UTF-1 27 csISO10646UTF1
+ISO_646.basic:1983 28 ref csISO646basic1983
+INVARIANT 29 csINVARIANT
+ISO_646.irv:1983 30 iso-ir-2 irv csISO2IntlRefVersion
+BS_4730 20 iso-ir-4 ISO646-GB gb uk csISO4UnitedKingdom
+NATS-SEFI 31 iso-ir-8-1 csNATSSEFI
+NATS-SEFI-ADD 32 iso-ir-8-2 csNATSSEFIADD
+NATS-DANO 33 iso-ir-9-1 csNATSDANO
+NATS-DANO-ADD 34 iso-ir-9-2 csNATSDANOADD
+SEN_850200_B 35 iso-ir-10 FI ISO646-FI ISO646-SE se csISO10Swedish
+SEN_850200_C 21 iso-ir-11 ISO646-SE2 se2 csISO11SwedishForNames
+KS_C_5601-1987 36 iso-ir-149 KS_C_5601-1989 KSC_5601 korean csKSC56011987
+ISO-2022-KR 37 csISO2022KR
+EUC-KR 38 csEUCKR EUCKR
+ISO-2022-JP 39 csISO2022JP
+ISO-2022-JP-2 40 csISO2022JP2
+ISO-2022-CN 104
+ISO-2022-CN-EXT 105
+JIS_C6220-1969-jp 41 JIS_C6220-1969 iso-ir-13 katakana x0201-7 csISO13JISC6220jp
+JIS_C6220-1969-ro 42 iso-ir-14 jp ISO646-JP csISO14JISC6220ro
+IT 22 iso-ir-15 ISO646-IT csISO15Italian
+PT 43 iso-ir-16 ISO646-PT csISO16Portuguese
+ES 23 iso-ir-17 ISO646-ES csISO17Spanish
+greek7-old 44 iso-ir-18 csISO18Greek7Old
+latin-greek 45 iso-ir-19 csISO19LatinGreek
+DIN_66003 24 iso-ir-21 de ISO646-DE csISO21German
+NF_Z_62-010_(1973) 46 iso-ir-25 ISO646-FR1 csISO25French
+Latin-greek-1 47 iso-ir-27 csISO27LatinGreek1
+ISO_5427 48 iso-ir-37 csISO5427Cyrillic
+JIS_C6226-1978 49 iso-ir-42 csISO42JISC62261978
+BS_viewdata 50 iso-ir-47 csISO47BSViewdata
+INIS 51 iso-ir-49 csISO49INIS
+INIS-8 52 iso-ir-50 csISO50INIS8
+INIS-cyrillic 53 iso-ir-51 csISO51INISCyrillic
+ISO_5427:1981 54 iso-ir-54 ISO5427Cyrillic1981
+ISO_5428:1980 55 iso-ir-55 csISO5428Greek
+GB_1988-80 56 iso-ir-57 cn ISO646-CN csISO57GB1988
+GB_2312-80 57 iso-ir-58 chinese csISO58GB231280
+NS_4551-1 25 iso-ir-60 ISO646-NO no csISO60DanishNorwegian csISO60Norwegian1
+NS_4551-2 58 ISO646-NO2 iso-ir-61 no2 csISO61Norwegian2
+NF_Z_62-010 26 iso-ir-69 ISO646-FR fr csISO69French
+videotex-suppl 59 iso-ir-70 csISO70VideotexSupp1
+PT2 60 iso-ir-84 ISO646-PT2 csISO84Portuguese2
+ES2 61 iso-ir-85 ISO646-ES2 csISO85Spanish2
+MSZ_7795.3 62 iso-ir-86 ISO646-HU hu csISO86Hungarian
+JIS_C6226-1983 63 iso-ir-87 x0208 JIS_X0208-1983 csISO87JISX0208
+greek7 64 iso-ir-88 csISO88Greek7
+ASMO_449 65 ISO_9036 arabic7 iso-ir-89 csISO89ASMO449
+iso-ir-90 66 csISO90
+JIS_C6229-1984-a 67 iso-ir-91 jp-ocr-a csISO91JISC62291984a
+JIS_C6229-1984-b 68 iso-ir-92 ISO646-JP-OCR-B jp-ocr-b csISO92JISC62991984b
+JIS_C6229-1984-b-add 69 iso-ir-93 jp-ocr-b-add csISO93JIS62291984badd
+JIS_C6229-1984-hand 70 iso-ir-94 jp-ocr-hand csISO94JIS62291984hand
+JIS_C6229-1984-hand-add 71 iso-ir-95 jp-ocr-hand-add csISO95JIS62291984handadd
+JIS_C6229-1984-kana 72 iso-ir-96 csISO96JISC62291984kana
+ISO_2033-1983 73 iso-ir-98 e13b csISO2033
+ANSI_X3.110-1983 74 iso-ir-99 CSA_T500-1983 NAPLPS csISO99NAPLPS
+ISO-8859-1 4 iso-ir-100 ISO_8859-1 ISO_8859-1:1987 latin1 l1 IBM819 CP819 csISOLatin1 8859_1 ISO8859-1
+ISO-8859-2 5 iso-ir-101 ISO_8859-2 ISO_8859-2:1987 latin2 l2 csISOLatin2 8859_2 ISO8859-2
+T.61-7bit 75 iso-ir-102 csISO102T617bit
+T.61-8bit 76 T.61 iso-ir-103 csISO103T618bit
+ISO-8859-3 6 iso-ir-109 ISO_8859-3 ISO_8859-3:1988 latin3 l3 csISOLatin3 8859_3 ISO8859-3
+ISO-8859-4 7 iso-ir-110 ISO_8859-4 ISO_8859-4:1988 latin4 l4 csISOLatin4 8859_4 ISO8859-4
+ECMA-cyrillic 77 iso-ir-111 KOI8-E csISO111ECMACyrillic
+CSA_Z243.4-1985-1 78 iso-ir-121 ISO646-CA csa7-1 ca csISO121Canadian1
+CSA_Z243.4-1985-2 79 iso-ir-122 ISO646-CA2 csa7-2 csISO122Canadian2
+CSA_Z243.4-1985-gr 80 iso-ir-123 csISO123CSAZ24341985gr
+ISO-8859-6 9 iso-ir-127 ISO_8859-6 ISO_8859-6:1987 ECMA-114 ASMO-708 arabic csISOLatinArabic
+ISO-8859-6-E 81 csISO88596E ISO_8859-6-E
+ISO-8859-6-I 82 csISO88596I ISO_8859-6-I
+ISO-8859-7 10 iso-ir-126 ISO_8859-7 ISO_8859-7:1987 ELOT_928 ECMA-118 greek greek8 csISOLatinGreek 8859_7 ISO8859-7
+T.101-G2 83 iso-ir-128 csISO128T101G2
+ISO-8859-8 11 iso-ir-138 ISO_8859-8 ISO_8859-8:1988 hebrew csISOLatinHebrew 8859_8 ISO8859-8
+ISO-8859-8-E 84 csISO88598E ISO_8859-8-E
+ISO-8859-8-I 85 csISO88598I ISO_8859-8-I
+CSN_369103 86 iso-ir-139 csISO139CSN369103
+JUS_I.B1.002 87 iso-ir-141 ISO646-YU js yu csISO141JUSIB1002
+ISO_6937-2-add 14 iso-ir-142 csISOTextComm
+IEC_P27-1 88 iso-ir-143 csISO143IECP271
+ISO-8859-5 8 iso-ir-144 ISO_8859-5 ISO_8859-5:1988 cyrillic csISOLatinCyrillic 8859_5 ISO8859-5
+JUS_I.B1.003-serb 89 iso-ir-146 serbian csISO146Serbian
+JUS_I.B1.003-mac 90 macedonian iso-ir-147 csISO147Macedonian
+ISO-8859-9 12 iso-ir-148 ISO_8859-9 ISO_8859-9:1989 latin5 l5 csISOLatin5 8859_9 ISO8859-9
+greek-ccitt 91 iso-ir-150 csISO150 csISO150GreekCCITT
+NC_NC00-10:81 92 cuba iso-ir-151 ISO646-CU csISO151Cuba
+ISO_6937-2-25 93 iso-ir-152 csISO6937Add
+GOST_19768-74 94 ST_SEV_358-88 iso-ir-153 csISO153GOST1976874
+ISO_8859-supp 95 iso-ir-154 latin1-2-5 csISO8859Supp
+ISO_10367-box 96 iso-ir-155 csISO10367Box
+ISO-8859-10 13 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6 8859_10 ISO8859-10
+latin-lap 97 lap iso-ir-158 csISO158Lap
+JIS_X0212-1990 98 x0212 iso-ir-159 csISO159JISX02121990
+DS_2089 99 DS2089 ISO646-DK dk csISO646Danish
+us-dk 100 csUSDK
+dk-us 101 csDKUS
+JIS_X0201 15 X0201 csHalfWidthKatakana
+KSC5636 102 ISO646-KR csKSC5636
+ISO-10646-UCS-2 1000 csUnicode UCS-2 UCS2
+ISO-10646-UCS-4 1001 csUCS4 UCS-4 UCS4
+DEC-MCS 2008 dec csDECMCS
+hp-roman8 2004 roman8 r8 csHPRoman8
+macintosh 2027 mac csMacintosh MACROMAN MAC-ROMAN X-MAC-ROMAN
+IBM037 2028 cp037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl csIBM037
+IBM038 2029 EBCDIC-INT cp038 csIBM038
+IBM273 2030 CP273 csIBM273
+IBM274 2031 EBCDIC-BE CP274 csIBM274
+IBM275 2032 EBCDIC-BR cp275 csIBM275
+IBM277 2033 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277
+IBM278 2034 CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278
+IBM280 2035 CP280 ebcdic-cp-it csIBM280
+IBM281 2036 EBCDIC-JP-E cp281 csIBM281
+IBM284 2037 CP284 ebcdic-cp-es csIBM284
+IBM285 2038 CP285 ebcdic-cp-gb csIBM285
+IBM290 2039 cp290 EBCDIC-JP-kana csIBM290
+IBM297 2040 cp297 ebcdic-cp-fr csIBM297
+IBM420 2041 cp420 ebcdic-cp-ar1 csIBM420
+IBM423 2042 cp423 ebcdic-cp-gr csIBM423
+IBM424 2043 cp424 ebcdic-cp-he csIBM424
+IBM437 2011 cp437 437 csPC8CodePage437
+IBM500 2044 CP500 ebcdic-cp-be ebcdic-cp-ch csIBM500
+IBM775 2087 cp775 csPC775Baltic
+IBM850 2009 cp850 850 csPC850Multilingual
+IBM851 2045 cp851 851 csIBM851
+IBM852 2010 cp852 852 csPCp852
+IBM855 2046 cp855 855 csIBM855
+IBM857 2047 cp857 857 csIBM857
+IBM860 2048 cp860 860 csIBM860
+IBM861 2049 cp861 861 cp-is csIBM861
+IBM862 2013 cp862 862 csPC862LatinHebrew
+IBM863 2050 cp863 863 csIBM863
+IBM864 2051 cp864 csIBM864
+IBM865 2052 cp865 865 csIBM865
+IBM866 2086 cp866 866 csIBM866
+IBM868 2053 CP868 cp-ar csIBM868
+IBM869 2054 cp869 869 cp-gr csIBM869
+IBM870 2055 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870
+IBM871 2056 CP871 ebcdic-cp-is csIBM871
+IBM880 2057 cp880 EBCDIC-Cyrillic csIBM880
+IBM891 2058 cp891 csIBM891
+IBM903 2059 cp903 csIBM903
+IBM904 2060 cp904 904 csIBBM904
+IBM905 2061 CP905 ebcdic-cp-tr csIBM905
+IBM918 2062 CP918 ebcdic-cp-ar2 csIBM918
+IBM1026 2063 CP1026 csIBM1026
+EBCDIC-AT-DE 2064 csIBMEBCDICATDE
+EBCDIC-AT-DE-A 2065 csEBCDICATDEA
+EBCDIC-CA-FR 2066 csEBCDICCAFR
+EBCDIC-DK-NO 2067 csEBCDICDKNO
+EBCDIC-DK-NO-A 2068 csEBCDICDKNOA
+EBCDIC-FI-SE 2069 csEBCDICFISE
+EBCDIC-FI-SE-A 2070 csEBCDICFISEA
+EBCDIC-FR 2071 csEBCDICFR
+EBCDIC-IT 2072 csEBCDICIT
+EBCDIC-PT 2073 csEBCDICPT
+EBCDIC-ES 2074 csEBCDICES
+EBCDIC-ES-A 2075 csEBCDICESA
+EBCDIC-ES-S 2076 csEBCDICESS
+EBCDIC-UK 2077 csEBCDICUK
+EBCDIC-US 2078 csEBCDICUS
+UNKNOWN-8BIT 2079 csUnknown8BiT
+MNEMONIC 2080 csMnemonic
+MNEM 2081 csMnem
+VISCII 2082 csVISCII
+VIQR 2083 csVIQR
+KOI8-R 2084 csKOI8R
+KOI8-U 2088
+IBM00858 2089 CCSID00858 CP00858 PC-Multilingual-850+euro
+IBM00924 2090 CCSID00924 CP00924 ebcdic-Latin9--euro
+IBM01140 2091 CCSID01140 CP01140 ebcdic-us-37+euro
+IBM01141 2092 CCSID01141 CP01141 ebcdic-de-273+euro
+IBM01142 2093 CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro
+IBM01143 2094 CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro
+IBM01144 2095 CCSID01144 CP01144 ebcdic-it-280+euro
+IBM01145 2096 CCSID01145 CP01145 ebcdic-es-284+euro
+IBM01146 2097 CCSID01146 CP01146 ebcdic-gb-285+euro
+IBM01147 2098 CCSID01147 CP01147 ebcdic-fr-297+euro
+IBM01148 2099 CCSID01148 CP01148 ebcdic-international-500+euro
+IBM01149 2100 CCSID01149 CP01149 ebcdic-is-871+euro
+Big5-HKSCS 2101
+IBM1047 2102 IBM-1047
+PTCP154 2103 csPTCP154 PT154 CP154 Cyrillic-Asian
+Amiga-1251 2104 Ami1251 Amiga1251 Ami-1251
+KOI7-switched 2105
+UNICODE-1-1 1010 csUnicode11
+SCSU 1011
+UTF-7 1012
+UTF-16BE 1013
+UTF-16LE 1014
+UTF-16 1015
+CESU-8 1016 csCESU-8
+UTF-32 1017
+UTF-32BE 1018
+UTF-32LE 1019
+BOCU-1 1020 csBOCU-1
+UNICODE-1-1-UTF-7 103 csUnicode11UTF7
+UTF-8 106 UNICODE-1-1-UTF-8 UNICODE-2-0-UTF-8 utf8
+ISO-8859-13 109 8859_13 ISO8859-13
+ISO-8859-14 110 iso-ir-199 ISO_8859-14:1998 ISO_8859-14 latin8 iso-celtic l8 8859_14 ISO8859-14
+ISO-8859-15 111 ISO_8859-15 Latin-9 8859_15 ISO8859-15
+ISO-8859-16 112 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10
+GBK 113 CP936 MS936 windows-936
+GB18030 114
+OSD_EBCDIC_DF04_15 115
+OSD_EBCDIC_DF03_IRV 116
+OSD_EBCDIC_DF04_1 117
+JIS_Encoding 16 csJISEncoding
+Shift_JIS 17 MS_Kanji csShiftJIS X-SJIS Shift-JIS
+EUC-JP 18 csEUCPkdFmtJapanese Extended_UNIX_Code_Packed_Format_for_Japanese EUCJP
+Extended_UNIX_Code_Fixed_Width_for_Japanese 19 csEUCFixWidJapanese
+ISO-10646-UCS-Basic 1002 csUnicodeASCII
+ISO-10646-Unicode-Latin1 1003 csUnicodeLatin1 ISO-10646
+ISO-Unicode-IBM-1261 1005 csUnicodeIBM1261
+ISO-Unicode-IBM-1268 1006 csUnicodeIBM1268
+ISO-Unicode-IBM-1276 1007 csUnicodeIBM1276
+ISO-Unicode-IBM-1264 1008 csUnicodeIBM1264
+ISO-Unicode-IBM-1265 1009 csUnicodeIBM1265
+ISO-8859-1-Windows-3.0-Latin-1 2000 csWindows30Latin1
+ISO-8859-1-Windows-3.1-Latin-1 2001 csWindows31Latin1
+ISO-8859-2-Windows-Latin-2 2002 csWindows31Latin2
+ISO-8859-9-Windows-Latin-5 2003 csWindows31Latin5
+Adobe-Standard-Encoding 2005 csAdobeStandardEncoding
+Ventura-US 2006 csVenturaUS
+Ventura-International 2007 csVenturaInternational
+PC8-Danish-Norwegian 2012 csPC8DanishNorwegian
+PC8-Turkish 2014 csPC8Turkish
+IBM-Symbols 2015 csIBMSymbols
+IBM-Thai 2016 csIBMThai
+HP-Legal 2017 csHPLegal
+HP-Pi-font 2018 csHPPiFont
+HP-Math8 2019 csHPMath8
+Adobe-Symbol-Encoding 2020 csHPPSMath
+HP-DeskTop 2021 csHPDesktop
+Ventura-Math 2022 csVenturaMath
+Microsoft-Publishing 2023 csMicrosoftPublishing
+Windows-31J 2024 csWindows31J
+GB2312 2025 csGB2312 EUC-CN EUCCN CN-GB
+Big5 2026 csBig5 BIG-FIVE BIG-5 CN-BIG5 BIG_FIVE x-x-big5
+windows-1250 2250 CP1250 MS-EE
+windows-1251 2251 CP1251 MS-CYRL
+windows-1252 2252 CP1252 MS-ANSI
+windows-1253 2253 CP1253 MS-GREEK
+windows-1254 2254 CP1254 MS-TURK
+windows-1255 2255
+windows-1256 2256 CP1256 MS-ARAB
+windows-1257 2257 CP1257 WINBALTRIM
+windows-1258 2258
+TIS-620 2259
+HZ-GB-2312 2085
+
+# Additional encodings not defined by IANA
+
+# Arbitrary allocations
+#CP737 3001
+#CP853 3002
+#CP856 3003
+CP874 3004 WINDOWS-874
+#CP922 3005
+#CP1046 3006
+#CP1124 3007
+#CP1125 3008 WINDOWS-1125
+#CP1129 3009
+#CP1133 3010 IBM-CP1133
+#CP1161 3011 IBM-1161 IBM1161 CSIBM1161
+#CP1162 3012 IBM-1162 IBM1162 CSIBM1162
+#CP1163 3013 IBM-1163 IBM1163 CSIBM1163
+#GEORGIAN-ACADEMY 3014
+#GEORGIAN-PS 3015
+#KOI8-RU 3016
+#KOI8-T 3017
+#MACARABIC 3018 X-MAC-ARABIC MAC-ARABIC
+#MACCROATIAN 3019 X-MAC-CROATIAN MAC-CROATIAN
+#MACGREEK 3020 X-MAC-GREEK MAC-GREEK
+#MACHEBREW 3021 X-MAC-HEBREW MAC-HEBREW
+#MACICELAND 3022 X-MAC-ICELAND MAC-ICELAND
+#MACROMANIA 3023 X-MAC-ROMANIA MAC-ROMANIA
+#MACTHAI 3024 X-MAC-THAI MAC-THAI
+#MACTURKISH 3025 X-MAC-TURKISH MAC-TURKISH
+#MULELAO-1 3026
+CP949 3027 WINDOWS-949
+
+# From Unicode Lib
+ISO-IR-182 4000
+ISO-IR-197 4002
+ISO-2022-JP-1 4008
+MACCYRILLIC 4009 X-MAC-CYRILLIC MAC-CYRILLIC
+MACUKRAINE 4010 X-MAC-UKRAINIAN MAC-UKRAINIAN
+MACCENTRALEUROPE 4011 X-MAC-CENTRALEURROMAN MAC-CENTRALEURROMAN
+JOHAB 4012
+ISO-8859-11 4014 iso-ir-166 ISO_8859-11 ISO8859-11 8859_11
+X-CURRENT 4999 X-SYSTEM
+X-ACORN-LATIN1 5001
+X-ACORN-FUZZY 5002
Index: test/run-single-test.sh
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ test/run-single-test.sh 2009-07-30 11:27:55.000000000 +0100
@@ -0,0 +1,144 @@
+#!/bin/bash
+#
+# This is a simple script to convert the a XML testcae to C source file, compile it, run it, and report the result.
+# Usage:
+# This script is designed to run under the libdom/test directory.
+#
+# domts="testcases" dtd="dom1-interfaces.xml" level="level1" ./run-single-test.sh
+#
+# The above command will convert the XML testcase in directory testcases/tests/level/core and
+# use dom1-interfaces.xml to convert it.
+# This script will generate a output/ directory in libdom/test, and in that directory, there is a same structure
+# as in DOMTS XML testcases files.
+#
+# This file is part of libdom test suite.
+# Licensed under the MIT License,
+# http://www.opensource.org/licenses/mit-license.php
+# Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+
+level=${level:-"level1"};
+module=${module:-"core"};
+domts=${domts:?"The \$domts must be assigned some value"};
+output=${output:-"output"};
+dtd=${dtd:?"The DTD file must be given"};
+
+testdir="$domts"/tests/"$level"/"$module"
+log="$output"/"$level"/"$module"/test.log;
+
+src="testutils/comparators.c testutils/domtsasserts.c testutils/foreach.c testutils/list.c testutils/load.c testutils/utils.c testutils/domtscondition.c"
+domdir="../build-Linux-Linux-debug-lib-static"
+ldflags="-L$domdir -ldom -L/usr/local/lib -lwapcaplet -L/usr/lib -lxml2 -lhubbub -lparserutils"
+#ldflags="-L/usr/lib -lm -lz -L$domdir -ldom -L/usr/local/lib -lwapcaplet -lxml2 -lhubbub -lparserutils"
+cflags="-Itestutils/ -I../bindings/xml -I../include -I../bindings/hubbub -I/usr/local/include"
+
+total=0;
+fail=0;
+pass=0;
+conversion=0;
+compile=0;
+run=0;
+nsupport=0;
+
+# Create the directories if necessary
+if [ ! -e "$ouput" ]; then
+ mkdir -p "$output";
+fi
+if [ ! -e "$level" ]; then
+ mkdir -p "$output"/"$level";
+fi
+if [ ! -e "$module" ]; then
+ mkdir -p "$output"/"$level"/"$module";
+fi
+
+# Prepare the test files
+if [ -e "files" ]; then
+ rm -fr files;
+fi
+cp -fr "$testdir/files" ./;
+
+while read test; do
+ total=$(($total+1));
+
+ file=`basename "$test"`;
+ name=${file%%.xml};
+
+ cfile="$output"/"$level"/"$module"/"$name".c;
+ ofile="$output"/"$level"/"$module"/"$name";
+ tfile="$testdir"/"$file";
+
+ echo -n "$file:";
+
+ # Generate the test C program
+ out=`perl transform.pl "$dtd" "$tfile" 2>&1 >"${cfile}.unindent"`;
+ if [ "$?" != "0" ]; then
+ fail=$((fail+1));
+ conversion=$((conversion+1));
+ echo "$tfile Conversion Error:" >& 3;
+ echo "Please make sure you have XML::XPath perl module installed!" >& 3;
+ echo "$out" >&3;
+ echo -e "----------------------------------------\n\n" >& 3;
+ echo "failed!";
+ rm -fr "${cfile}.unindent";
+ continue;
+ fi
+ out=`indent "${cfile}.unindent" -o "$cfile" 2>&1`;
+ if [ "$?" != "0" ]; then
+ rm -fr "${cfile}.unindent";
+ fail=$((fail+1));
+ conversion=$((conversion+1));
+ echo "$tfile Conversion Error:" >& 3;
+ echo "$out" >& 3;
+ echo -e "----------------------------------------\n\n" >& 3;
+ echo "failed!";
+ continue;
+ fi
+ rm -fr "${cfile}.unindent";
+
+ # Compile it now
+ out=` ( gcc -g $cflags $src $cfile $ldflags -o "$ofile" ) 2>&1`;
+ if [ "$?" != "0" ]; then
+ fail=$((fail+1));
+ compile=$((compile+1));
+ echo "$tfile Compile Error:" >& 3;
+ echo "$out" >& 3;
+ echo -e "----------------------------------------\n\n" >& 3;
+ echo "failed!";
+ continue;
+ fi
+
+ # Run the test and test the result
+ cd files;
+ out=$(../$ofile 2>&1);
+ ret="$?";
+ if [ "$ret" != "0" ]; then
+ cd ..;
+ fail=$((fail+1));
+ if [ "$ret" == "9" ]; then
+ nsupport=$((nsupport+1))
+ echo "$tfile Not Support:" >& 3;
+ echo "$out" >& 3;
+ echo -e "----------------------------------------\n\n" >& 3;
+ echo "not supported!";
+ else
+ run=$((run+1));
+ echo "$tfile Run Error:" >& 3;
+ echo "$out" >& 3;
+ echo -e "----------------------------------------\n\n" >& 3;
+ echo "failed!";
+ fi
+ continue;
+ fi
+ cd ..;
+
+ pass=$((pass+1));
+ echo "passed.";
+
+done 3>&1 < <(echo $1);
+
+echo "Total: $total";
+echo "Passed: $pass";
+echo "Failed: $fail";
+echo "Conversion Error: $conversion";
+echo "Compile Error: $compile";
+echo "Run Error: $run";
+echo "Not Support: $nsupport";
Index: include/dom/core/typeinfo.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ include/dom/core/typeinfo.h 2009-07-30 11:28:50.000000000 +0100
@@ -0,0 +1,47 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ */
+
+#ifndef dom_core_typeinfo_h_
+#define dom_core_typeinfo_h_
+
+#include <stdbool.h>
+
+#include <dom/core/exceptions.h>
+
+struct dom_string;
+
+typedef struct dom_type_info dom_type_info;
+
+typedef enum {
+ DERIVATION_RESTRICTION = 0x00000001,
+ DERIVATION_EXTENSION = 0x00000002,
+ DERIVATION_UNION = 0x00000004,
+ DERIVATION_LIST = 0x00000008
+} dom_type_info_derivation_method;
+
+dom_exception _dom_type_info_get_type_name(dom_type_info *ti,
+ struct dom_string **ret);
+#define dom_type_info_get_type_name(t, r) _dom_type_info_get_type_name( \
+ (dom_type_info *) (t), (struct dom_string **) (r))
+
+
+dom_exception _dom_type_info_get_type_namespace(dom_type_info *ti,
+ struct dom_string **ret);
+#define dom_type_info_get_type_namespace(t, r) _dom_type_info_get_type_namespace(\
+ (dom_type_info *) (t), (struct dom_string **) (r))
+
+
+dom_exception _dom_type_info_is_derived(dom_type_info *ti,
+ struct dom_string *namespace, struct dom_string *name,
+ dom_type_info_derivation_method method, bool *ret);
+#define dom_type_info_is_derived(t, s, n, m, r) _dom_type_info_is_derived(\
+ (dom_type_info *) (t), (struct dom_string *) (s), \
+ (struct dom_string *) (n), (dom_type_info_derivation_method) (m)\
+ (bool *) (r))
+
+
+#endif
Index: include/dom/core/comment.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ include/dom/core/comment.h 2009-07-30 11:28:50.000000000 +0100
@@ -0,0 +1,18 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ */
+
+#ifndef dom_core_comment_h_
+#define dom_core_comment_h_
+
+#include <stdbool.h>
+
+#include <dom/core/exceptions.h>
+#include <dom/core/characterdata.h>
+
+typedef struct dom_comment dom_comment;
+
+#endif
Index: src/utils/validate.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ src/utils/validate.h 2009-07-30 11:28:52.000000000 +0100
@@ -0,0 +1,26 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ *
+ * This file contains the API used to validate whether certain element's
+ * name/namespace are legal according the XML 1.0 standard. See
+ *
+ * http://www.w3.org/TR/2004/REC-xml-20040204/
+ *
+ * for detail.
+ */
+
+#ifndef dom_utils_valid_h_
+#define dom_utils_valid_h_
+
+#include <stdbool.h>
+
+struct dom_string;
+
+bool _dom_validate_name(struct dom_string *name);
+bool _dom_validate_ncname(struct dom_string *name);
+
+#endif
+
Index: src/utils/character_valid.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ src/utils/character_valid.h 2009-07-30 11:28:52.000000000 +0100
@@ -0,0 +1,51 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ *
+ * This file contains the API used to validate whether certain character in
+ * name/value is legal according the XML 1.0 standard. See
+ *
+ * http://www.w3.org/TR/2004/REC-xml-20040204/
+ * http://www.w3.org/TR/REC-xml/
+ *
+ * for detail.
+ */
+
+#ifndef dom_utils_character_valid_h_
+#define dom_utils_character_valid_h_
+
+#include <stdbool.h>
+
+struct xml_char_range {
+ unsigned int start;
+ unsigned int end;
+};
+
+struct xml_char_group {
+ int len;
+ const struct xml_char_range *range;
+};
+
+/* The groups */
+extern const struct xml_char_group base_char_group;
+extern const struct xml_char_group char_group;
+extern const struct xml_char_group combining_char_group;
+extern const struct xml_char_group digit_char_group;
+extern const struct xml_char_group extender_group;
+extern const struct xml_char_group ideographic_group;
+
+bool _dom_is_character_in_group(unsigned int ch, const struct xml_char_group *group);
+
+#define is_base_char(ch) _dom_is_character_in_group((ch), &base_char_group)
+#define is_char(ch) _dom_is_character_in_group((ch), &char_group)
+#define is_combining_char(ch) _dom_is_character_in_group((ch), &combining_char_group)
+#define is_digit(ch) _dom_is_character_in_group((ch), &digit_char_group)
+#define is_extender(ch) _dom_is_character_in_group((ch), &extender_group)
+#define is_ideographic(ch) _dom_is_character_in_group((ch), &ideographic_group)
+
+#define is_letter(ch) (is_base_char(ch) || is_ideographic(ch))
+
+#endif
+
Index: src/utils/hashtable.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ src/utils/hashtable.h 2009-07-30 11:28:52.000000000 +0100
@@ -0,0 +1,40 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2006 Rob Kendrick <rjek(a)rjek.com>
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ */
+
+#ifndef dom_utils_hashtable_h_
+#define dom_utils_hashtable_h_
+
+#include <stdbool.h>
+#include <dom/functypes.h>
+
+typedef struct hash_table hash_table;
+/* The hash function */
+typedef unsigned int (*hash_func)(void *key);
+/* Function to clone/delete key */
+typedef void *(*key_func)(void *key, void *pw, dom_alloc alloc, void *alloc_pw,
+ bool clone);
+/* Function to clone/delete value */
+typedef void *(*value_func)(void *value, void *pw, dom_alloc alloc,
+ void *alloc_pw, bool clone);
+
+struct hash_table *hash_create(unsigned int chains, hash_func hash,
+ dom_alloc alloc, void *ptr);
+struct hash_table *hash_clone(struct hash_table *ht, dom_alloc alloc,
+ void *pw, key_func kf, void *key_pw, value_func vf, void *value_pw);
+void hash_destroy(struct hash_table *ht, key_func kf, void *key_pw,
+ value_func vf, void *value_pw);
+bool hash_add(struct hash_table *ht, void *key, void *value, bool replace);
+void *hash_get(struct hash_table *ht, void *key);
+void *hash_del(struct hash_table *ht, void *key);
+void *hash_iterate(struct hash_table *ht, unsigned int *c1,
+ unsigned int **c2);
+unsigned int hash_get_length(struct hash_table *ht);
+unsigned int hash_get_chains(struct hash_table *ht);
+hash_func hash_get_func(struct hash_table *ht);
+
+#endif
Index: src/utils/list.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ src/utils/list.h 2009-07-30 11:28:52.000000000 +0100
@@ -0,0 +1,61 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ *
+ * This file contains the list structure used to compose lists.
+ *
+ * Note: This is a implementation of a doubld-linked cyclar list.
+ */
+
+#ifndef dom_utils_list_h_
+#define dom_utils_list_h_
+
+#include <stddef.h>
+
+struct list_entry {
+ struct list_entry *prev;
+ struct list_entry *next;
+};
+
+/**
+ * Initilise a list_entry structure
+ */
+static inline void list_init(struct list_entry *ent)
+{
+ ent->prev = ent;
+ ent->next = ent;
+}
+
+/**
+ * Append a new list_entry after the list
+ *
+ * \param head The list header
+ * \param new The new entry
+ *
+ * Note: Append a new entry to list started with head.
+ */
+static inline void list_append(struct list_entry *head, struct list_entry *new)
+{
+ new->next = head;
+ new->prev = head->prev;
+ head->prev->next = new;
+ head->prev = new;
+}
+
+/**
+ * Delete a list_entry from the list
+ *
+ * \param entry The entry need to be deleted from the list
+ */
+static inline void list_del(struct list_entry *ent)
+{
+ ent->prev->next = ent->next;
+ ent->next->prev = ent->prev;
+
+ ent->prev = ent;
+ ent->next = ent;
+}
+
+#endif
Index: src/utils/resource_mgr.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ src/utils/resource_mgr.h 2009-07-30 11:28:52.000000000 +0100
@@ -0,0 +1,40 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ */
+
+#ifndef dom_utils_resource_mgr_h_
+#define dom_utils_resource_mgr_h_
+
+#include <dom/functypes.h>
+#include <dom/core/exceptions.h>
+
+#include "hashtable.h"
+
+struct lwc_context_s;
+struct lwc_string_s;
+struct dom_string;
+
+typedef struct resource_mgr {
+ dom_alloc alloc;
+ void *pw;
+ struct lwc_context_s *ctx;
+} resource_mgr;
+
+void *resource_mgr_alloc(struct resource_mgr *res, void *ptr, size_t size);
+
+dom_exception resource_mgr_create_string(struct resource_mgr *res,
+ const uint8_t *data, size_t len, struct dom_string **result);
+
+dom_exception resource_mgr_create_lwcstring(struct resource_mgr *res,
+ const uint8_t *data, size_t len, struct lwc_string_s **result);
+
+dom_exception resource_mgr_create_string_from_lwcstring(struct resource_mgr *res,
+ struct lwc_string_s *str, struct dom_string **result);
+
+dom_exception resource_mgr_create_hashtable(struct resource_mgr *res,
+ size_t chains, hash_func f, struct hash_table **ht);
+
+#endif
Index: src/utils/validate.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ src/utils/validate.c 2009-07-30 11:28:52.000000000 +0100
@@ -0,0 +1,174 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ */
+
+#include <inttypes.h>
+#include <stddef.h>
+
+#include "utils/validate.h"
+
+#include <dom/core/string.h>
+
+#include "utils/character_valid.h"
+#include "utils/namespace.h"
+#include "utils/utils.h"
+
+/* An combination of various tests */
+static bool is_first_char(uint32_t ch);
+static bool is_name_char(uint32_t ch);
+
+/* Test whether the character can be the first character of
+ * a NCName.
+ */
+static bool is_first_char(uint32_t ch)
+{
+ /* Refer http://www.w3.org/TR/REC-xml/ for detail */
+ if (((ch >= 'a') && (ch <= 'z')) ||
+ ((ch >= 'A') && (ch <= 'Z')) ||
+ (ch == '_') || (ch == ':') ||
+ ((ch >= 0xC0) && (ch <= 0xD6)) ||
+ ((ch >= 0xD8) && (ch <= 0xF6)) ||
+ ((ch >= 0xF8) && (ch <= 0x2FF)) ||
+ ((ch >= 0x370) && (ch <= 0x37D)) ||
+ ((ch >= 0x37F) && (ch <= 0x1FFF)) ||
+ ((ch >= 0x200C) && (ch <= 0x200D)) ||
+ ((ch >= 0x2070) && (ch <= 0x218F)) ||
+ ((ch >= 0x2C00) && (ch <= 0x2FEF)) ||
+ ((ch >= 0x3001) && (ch <= 0xD7FF)) ||
+ ((ch >= 0xF900) && (ch <= 0xFDCF)) ||
+ ((ch >= 0xFDF0) && (ch <= 0xFFFD)) ||
+ ((ch >= 0x10000) && (ch <= 0xEFFFF)))
+ return true;
+
+ if (is_letter(ch) || ch == (uint32_t) '_' || ch == (uint32_t) ':') {
+ return true;
+ }
+
+ return false;
+}
+
+/* Test whether the character can be a part of a NCName */
+static bool is_name_char(uint32_t ch)
+{
+ /* Refer http://www.w3.org/TR/REC-xml/ for detail */
+ if (((ch >= 'a') && (ch <= 'z')) ||
+ ((ch >= 'A') && (ch <= 'Z')) ||
+ ((ch >= '0') && (ch <= '9')) || /* !start */
+ (ch == '_') || (ch == ':') ||
+ (ch == '-') || (ch == '.') || (ch == 0xB7) || /* !start */
+ ((ch >= 0xC0) && (ch <= 0xD6)) ||
+ ((ch >= 0xD8) && (ch <= 0xF6)) ||
+ ((ch >= 0xF8) && (ch <= 0x2FF)) ||
+ ((ch >= 0x300) && (ch <= 0x36F)) || /* !start */
+ ((ch >= 0x370) && (ch <= 0x37D)) ||
+ ((ch >= 0x37F) && (ch <= 0x1FFF)) ||
+ ((ch >= 0x200C) && (ch <= 0x200D)) ||
+ ((ch >= 0x203F) && (ch <= 0x2040)) || /* !start */
+ ((ch >= 0x2070) && (ch <= 0x218F)) ||
+ ((ch >= 0x2C00) && (ch <= 0x2FEF)) ||
+ ((ch >= 0x3001) && (ch <= 0xD7FF)) ||
+ ((ch >= 0xF900) && (ch <= 0xFDCF)) ||
+ ((ch >= 0xFDF0) && (ch <= 0xFFFD)) ||
+ ((ch >= 0x10000) && (ch <= 0xEFFFF)))
+ return true;
+
+ if (is_letter(ch) == true)
+ return true;
+ if (is_digit(ch) == true)
+ return true;
+ if (is_combining_char(ch) == true)
+ return true;
+ if (is_extender(ch) == true)
+ return true;
+
+ if (ch == (uint32_t) '.' || ch == (uint32_t) '-' ||
+ ch == (uint32_t) '_' || ch == (uint32_t) ':')
+ return true;
+
+ return false;
+}
+
+/* Test whether the name is a valid one according XML 1.0 standard.
+ * For the standard please refer:
+ *
+ * http://www.w3.org/TR/2004/REC-xml-20040204/
+ *
+ * \param name The name need to be tested
+ */
+bool _dom_validate_name(struct dom_string *name)
+{
+ uint32_t ch, len, i;
+ dom_exception err;
+
+ if (name == NULL)
+ return false;
+
+ len = dom_string_length(name);
+ if (len == 0)
+ return false;
+
+ /* Test the first character of this string */
+ err = dom_string_at(name, 0, &ch);
+ if (err != DOM_NO_ERR)
+ return false;
+
+ if (is_first_char(ch) == false)
+ return false;
+
+ /* Test all remain characters in this string */
+ for(i = 1; i < len; i++) {
+ err = dom_string_at(name, i, &ch);
+ if (err != DOM_NO_ERR)
+ return false;
+
+ if (is_name_char(ch) != true)
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Validate whether the string is a legal NCName.
+ * Refer http://www.w3.org/TR/REC-xml-names/ for detail.
+ *
+ * \param str The name to validate
+ */
+bool _dom_validate_ncname(struct dom_string *name)
+{
+ uint32_t ch, len, i;
+ dom_exception err;
+
+ if (name == NULL)
+ return false;
+
+ len = dom_string_length(name);
+ if (len == 0)
+ return false;
+
+ /* Test the first character of this string */
+ err = dom_string_at(name, 0, &ch);
+ if (err != DOM_NO_ERR)
+ return false;
+
+ if (is_letter(ch) == false && ch != (uint32_t) '_')
+ return false;
+
+ /* Test all remain characters in this string */
+ for(i = 1; i < len; i++) {
+ err = dom_string_at(name, i, &ch);
+ if (err != DOM_NO_ERR)
+ return false;
+
+ if (is_name_char(ch) == false)
+ return false;
+
+ if (ch == (uint32_t) ':')
+ return false;
+ }
+
+ return true;
+}
Index: src/utils/character_valid.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ src/utils/character_valid.c 2009-07-30 11:28:52.000000000 +0100
@@ -0,0 +1,213 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ */
+
+#include "utils/character_valid.h"
+
+#include <assert.h>
+
+static const struct xml_char_range base_char_range[] = { {0x41, 0x5a},
+ {0x61, 0x7a}, {0xc0, 0xd6}, {0xd8, 0xf6}, {0x00f8, 0x00ff}, {0x100, 0x131},
+ {0x134, 0x13e}, {0x141, 0x148}, {0x14a, 0x17e}, {0x180, 0x1c3},
+ {0x1cd, 0x1f0}, {0x1f4, 0x1f5}, {0x1fa, 0x217}, {0x250, 0x2a8},
+ {0x2bb, 0x2c1}, {0x386, 0x386}, {0x388, 0x38a}, {0x38c, 0x38c},
+ {0x38e, 0x3a1}, {0x3a3, 0x3ce}, {0x3d0, 0x3d6}, {0x3da, 0x3da},
+ {0x3dc, 0x3dc}, {0x3de, 0x3de}, {0x3e0, 0x3e0}, {0x3e2, 0x3f3},
+ {0x401, 0x40c}, {0x40e, 0x44f}, {0x451, 0x45c}, {0x45e, 0x481},
+ {0x490, 0x4c4}, {0x4c7, 0x4c8}, {0x4cb, 0x4cc}, {0x4d0, 0x4eb},
+ {0x4ee, 0x4f5}, {0x4f8, 0x4f9}, {0x531, 0x556}, {0x559, 0x559},
+ {0x561, 0x586}, {0x5d0, 0x5ea}, {0x5f0, 0x5f2}, {0x621, 0x63a},
+ {0x641, 0x64a}, {0x671, 0x6b7}, {0x6ba, 0x6be}, {0x6c0, 0x6ce},
+ {0x6d0, 0x6d3}, {0x6d5, 0x6d5}, {0x6e5, 0x6e6}, {0x905, 0x939},
+ {0x93d, 0x93d}, {0x958, 0x961}, {0x985, 0x98c}, {0x98f, 0x990},
+ {0x993, 0x9a8}, {0x9aa, 0x9b0}, {0x9b2, 0x9b2}, {0x9b6, 0x9b9},
+ {0x9dc, 0x9dd}, {0x9df, 0x9e1}, {0x9f0, 0x9f1}, {0xa05, 0xa0a},
+ {0xa0f, 0xa10}, {0xa13, 0xa28}, {0xa2a, 0xa30}, {0xa32, 0xa33},
+ {0xa35, 0xa36}, {0xa38, 0xa39}, {0xa59, 0xa5c}, {0xa5e, 0xa5e},
+ {0xa72, 0xa74}, {0xa85, 0xa8b}, {0xa8d, 0xa8d}, {0xa8f, 0xa91},
+ {0xa93, 0xaa8}, {0xaaa, 0xab0}, {0xab2, 0xab3}, {0xab5, 0xab9},
+ {0xabd, 0xabd}, {0xae0, 0xae0}, {0xb05, 0xb0c}, {0xb0f, 0xb10},
+ {0xb13, 0xb28}, {0xb2a, 0xb30}, {0xb32, 0xb33}, {0xb36, 0xb39},
+ {0xb3d, 0xb3d}, {0xb5c, 0xb5d}, {0xb5f, 0xb61}, {0xb85, 0xb8a},
+ {0xb8e, 0xb90}, {0xb92, 0xb95}, {0xb99, 0xb9a}, {0xb9c, 0xb9c},
+ {0xb9e, 0xb9f}, {0xba3, 0xba4}, {0xba8, 0xbaa}, {0xbae, 0xbb5},
+ {0xbb7, 0xbb9}, {0xc05, 0xc0c}, {0xc0e, 0xc10}, {0xc12, 0xc28},
+ {0xc2a, 0xc33}, {0xc35, 0xc39}, {0xc60, 0xc61}, {0xc85, 0xc8c},
+ {0xc8e, 0xc90}, {0xc92, 0xca8}, {0xcaa, 0xcb3}, {0xcb5, 0xcb9},
+ {0xcde, 0xcde}, {0xce0, 0xce1}, {0xd05, 0xd0c}, {0xd0e, 0xd10},
+ {0xd12, 0xd28}, {0xd2a, 0xd39}, {0xd60, 0xd61}, {0xe01, 0xe2e},
+ {0xe30, 0xe30}, {0xe32, 0xe33}, {0xe40, 0xe45}, {0xe81, 0xe82},
+ {0xe84, 0xe84}, {0xe87, 0xe88}, {0xe8a, 0xe8a}, {0xe8d, 0xe8d},
+ {0xe94, 0xe97}, {0xe99, 0xe9f}, {0xea1, 0xea3}, {0xea5, 0xea5},
+ {0xea7, 0xea7}, {0xeaa, 0xeab}, {0xead, 0xeae}, {0xeb0, 0xeb0},
+ {0xeb2, 0xeb3}, {0xebd, 0xebd}, {0xec0, 0xec4}, {0xf40, 0xf47},
+ {0xf49, 0xf69}, {0x10a0, 0x10c5}, {0x10d0, 0x10f6}, {0x1100, 0x1100},
+ {0x1102, 0x1103}, {0x1105, 0x1107}, {0x1109, 0x1109}, {0x110b, 0x110c},
+ {0x110e, 0x1112}, {0x113c, 0x113c}, {0x113e, 0x113e}, {0x1140, 0x1140},
+ {0x114c, 0x114c}, {0x114e, 0x114e}, {0x1150, 0x1150}, {0x1154, 0x1155},
+ {0x1159, 0x1159}, {0x115f, 0x1161}, {0x1163, 0x1163}, {0x1165, 0x1165},
+ {0x1167, 0x1167}, {0x1169, 0x1169}, {0x116d, 0x116e}, {0x1172, 0x1173},
+ {0x1175, 0x1175}, {0x119e, 0x119e}, {0x11a8, 0x11a8}, {0x11ab, 0x11ab},
+ {0x11ae, 0x11af}, {0x11b7, 0x11b8}, {0x11ba, 0x11ba}, {0x11bc, 0x11c2},
+ {0x11eb, 0x11eb}, {0x11f0, 0x11f0}, {0x11f9, 0x11f9}, {0x1e00, 0x1e9b},
+ {0x1ea0, 0x1ef9}, {0x1f00, 0x1f15}, {0x1f18, 0x1f1d}, {0x1f20, 0x1f45},
+ {0x1f48, 0x1f4d}, {0x1f50, 0x1f57}, {0x1f59, 0x1f59}, {0x1f5b, 0x1f5b},
+ {0x1f5d, 0x1f5d}, {0x1f5f, 0x1f7d}, {0x1f80, 0x1fb4}, {0x1fb6, 0x1fbc},
+ {0x1fbe, 0x1fbe}, {0x1fc2, 0x1fc4}, {0x1fc6, 0x1fcc}, {0x1fd0, 0x1fd3},
+ {0x1fd6, 0x1fdb}, {0x1fe0, 0x1fec}, {0x1ff2, 0x1ff4}, {0x1ff6, 0x1ffc},
+ {0x2126, 0x2126}, {0x212a, 0x212b}, {0x212e, 0x212e}, {0x2180, 0x2182},
+ {0x3041, 0x3094}, {0x30a1, 0x30fa}, {0x3105, 0x312c}, {0xac00, 0xd7a3}
+};
+
+const struct xml_char_group base_char_group = {
+ sizeof(base_char_range) / sizeof(base_char_range[0]), base_char_range};
+
+static const struct xml_char_range char_range[] = { {0x100, 0xd7ff},
+ {0xe000, 0xfffd}, {0x10000, 0x10ffff}
+};
+
+const struct xml_char_group char_group = {
+ sizeof(char_range) / sizeof(char_range[0]), char_range};
+
+static const struct xml_char_range combining_char_range[] = { {0x300, 0x345},
+ {0x360, 0x361}, {0x483, 0x486}, {0x591, 0x5a1}, {0x5a3, 0x5b9},
+ {0x5bb, 0x5bd}, {0x5bf, 0x5bf}, {0x5c1, 0x5c2}, {0x5c4, 0x5c4},
+ {0x64b, 0x652}, {0x670, 0x670}, {0x6d6, 0x6dc}, {0x6dd, 0x6df},
+ {0x6e0, 0x6e4}, {0x6e7, 0x6e8}, {0x6ea, 0x6ed}, {0x901, 0x903},
+ {0x93c, 0x93c}, {0x93e, 0x94c}, {0x94d, 0x94d}, {0x951, 0x954},
+ {0x962, 0x963}, {0x981, 0x983}, {0x9bc, 0x9bc}, {0x9be, 0x9be},
+ {0x9bf, 0x9bf}, {0x9c0, 0x9c4}, {0x9c7, 0x9c8}, {0x9cb, 0x9cd},
+ {0x9d7, 0x9d7}, {0x9e2, 0x9e3}, {0xa02, 0xa02}, {0xa3c, 0xa3c},
+ {0xa3e, 0xa3e}, {0xa3f, 0xa3f}, {0xa40, 0xa42}, {0xa47, 0xa48},
+ {0xa4b, 0xa4d}, {0xa70, 0xa71}, {0xa81, 0xa83}, {0xabc, 0xabc},
+ {0xabe, 0xac5}, {0xac7, 0xac9}, {0xacb, 0xacd}, {0xb01, 0xb03},
+ {0xb3c, 0xb3c}, {0xb3e, 0xb43}, {0xb47, 0xb48}, {0xb4b, 0xb4d},
+ {0xb56, 0xb57}, {0xb82, 0xb83}, {0xbbe, 0xbc2}, {0xbc6, 0xbc8},
+ {0xbca, 0xbcd}, {0xbd7, 0xbd7}, {0xc01, 0xc03}, {0xc3e, 0xc44},
+ {0xc46, 0xc48}, {0xc4a, 0xc4d}, {0xc55, 0xc56}, {0xc82, 0xc83},
+ {0xcbe, 0xcc4}, {0xcc6, 0xcc8}, {0xcca, 0xccd}, {0xcd5, 0xcd6},
+ {0xd02, 0xd03}, {0xd3e, 0xd43}, {0xd46, 0xd48}, {0xd4a, 0xd4d},
+ {0xd57, 0xd57}, {0xe31, 0xe31}, {0xe34, 0xe3a}, {0xe47, 0xe4e},
+ {0xeb1, 0xeb1}, {0xeb4, 0xeb9}, {0xebb, 0xebc}, {0xec8, 0xecd},
+ {0xf18, 0xf19}, {0xf35, 0xf35}, {0xf37, 0xf37}, {0xf39, 0xf39},
+ {0xf3e, 0xf3e}, {0xf3f, 0xf3f}, {0xf71, 0xf84}, {0xf86, 0xf8b},
+ {0xf90, 0xf95}, {0xf97, 0xf97}, {0xf99, 0xfad}, {0xfb1, 0xfb7},
+ {0xfb9, 0xfb9}, {0x20d0, 0x20dc}, {0x20e1, 0x20e1}, {0x302a, 0x302f},
+ {0x3099, 0x3099}, {0x309a, 0x309a}
+};
+
+const struct xml_char_group combining_char_group = {
+ sizeof(combining_char_range) / sizeof(combining_char_range[0]),
+ combining_char_range };
+
+static const struct xml_char_range digit_char_range[] = { {0x30, 0x39},
+ {0x660, 0x669}, {0x6f0, 0x6f9}, {0x966, 0x96f}, {0x9e6, 0x9ef},
+ {0xa66, 0xa6f}, {0xae6, 0xaef}, {0xb66, 0xb6f}, {0xbe7, 0xbef},
+ {0xc66, 0xc6f}, {0xce6, 0xcef}, {0xd66, 0xd6f}, {0xe50, 0xe59},
+ {0xed0, 0xed9}, {0xf20, 0xf29}
+};
+
+const struct xml_char_group digit_char_group = {
+ sizeof(digit_char_range) / sizeof(digit_char_range[0]),
+ digit_char_range };
+
+static const struct xml_char_range extender_range[] = { {0xb7, 0xb7},
+ {0x2d0, 0x2d0}, {0x2d1, 0x2d1}, {0x387, 0x387}, {0x640, 0x640},
+ {0xe46, 0xe46}, {0xec6, 0xec6}, {0x3005, 0x3005}, {0x3031, 0x3035},
+ {0x309d, 0x309e}, {0x30fc, 0x30fe}
+};
+
+const struct xml_char_group extender_group = {
+ sizeof(extender_range) / sizeof(extender_range[0]), extender_range };
+
+static const struct xml_char_range ideographic_range[] = { {0x3007, 0x3007},
+ {0x3021, 0x3029}, {0x4e00, 0x9fa5}
+};
+
+const struct xml_char_group ideographic_group = {
+ sizeof(ideographic_range) / sizeof(ideographic_range[0]),
+ ideographic_range };
+
+/* The binary search helper function */
+static bool binary_search(unsigned int ch, int left, int right,
+ const struct xml_char_range *range);
+
+/* Search for ch in range[left, right]
+ */
+bool binary_search(unsigned int ch, int left, int right,
+ const struct xml_char_range *range)
+{
+ if (left > right)
+ return false;
+
+ int mid = (left + right) / 2;
+ if (ch >= range[mid].start && ch <= range[mid].end)
+ return true;
+
+ if (ch < range[mid].start)
+ return binary_search(ch, left, mid - 1, range);
+
+ if (ch > range[mid].end)
+ return binary_search(ch, mid + 1, right, range);
+
+ return false;
+}
+
+/* Test whether certain character belongs to some XML character group
+ *
+ * \param ch The being tested character.
+ * \param group The character group.
+ * \return True if the character belongs to the group, false otherwise.
+ *
+ * Generally, we use an algorithm like binary search to find the desired
+ * character in the group. The time complexity is about lg(n) and here n is
+ * at most 180, so, I think the algorithm is fast enough for name validation.
+ */
+bool _dom_is_character_in_group(unsigned int ch, const struct xml_char_group *group)
+{
+ int len = group->len;
+ const struct xml_char_range *range = group->range;
+
+ if (ch < range[0].start || ch > range[len-1].end)
+ return false;
+
+ return binary_search(ch, 0, len - 1, range);
+}
+
+#ifdef CHVALID_DEBUG
+/* The following is the testcases for this file.
+ * Compile this file :
+ *
+ * gcc -o test -DCHVALID_DEBUG character_valid.c
+ *
+ */
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+ unsigned int ch = 0x666;
+
+ assert(is_digit(ch) == true);
+ assert(is_base_char(ch) == false);
+ assert(is_char(ch) == true);
+ assert(is_extender(ch) == false);
+ assert(is_combining_char(ch) == false);
+ assert(is_ideographic(ch) == false);
+
+ ch = 0xf40;
+
+ assert(is_digit(ch) == false);
+ assert(is_base_char(ch) == true);
+ assert(is_char(ch) == true);
+ assert(is_extender(ch) == false);
+ assert(is_combining_char(ch) == false);
+ assert(is_ideographic(ch) == false);
+
+ printf("The test pass.\n");
+ return 0;
+}
+
+#endif
Index: src/utils/hashtable.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ src/utils/hashtable.c 2009-07-30 11:28:52.000000000 +0100
@@ -0,0 +1,486 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2006 Rob Kendrick <rjek(a)rjek.com>
+ * Copyright 2006 Richard Wilson <info(a)tinct.net>
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <assert.h>
+#ifdef TEST_RIG
+#include <stdio.h>
+#endif
+#include "utils/hashtable.h"
+
+
+struct hash_entry {
+ void *key; /**< The key pointer */
+ void *value; /**< The value pointer */
+ struct hash_entry *next; /**< next entry */
+};
+
+struct hash_table {
+ unsigned int nchains;
+ hash_func hash;
+ struct hash_entry **chain;
+ unsigned int number; /**< The enries in this table */
+
+ dom_alloc alloc;
+ void *ptr;
+};
+
+
+/**
+ * Create a new hash table, and return a context for it. The memory consumption
+ * of a hash table is approximately 8 + (nchains * 12) bytes if it is empty.
+ *
+ * \param chains Number of chains/buckets this hash table will have. This
+ * should be a prime number, and ideally a prime number just
+ * over a power of two, for best performance and distribution.
+ * \param hash The hash function
+ * \param alloc The memory allocator
+ * \param ptr The private pointer for the allocator
+ * \return struct hash_table containing the context of this hash table or NULL
+ * if there is insufficent memory to create it and its chains.
+ */
+
+struct hash_table *hash_create(unsigned int chains, hash_func hash,
+ dom_alloc alloc, void *ptr)
+{
+ struct hash_table *r = alloc(NULL, sizeof(struct hash_table), ptr);
+
+ if (r == NULL) {
+ return NULL;
+ }
+
+ r->nchains = chains;
+ r->hash = hash;
+ r->alloc = alloc;
+ r->ptr = ptr;
+ r->chain = (struct hash_entry **)alloc(NULL,
+ chains*sizeof(struct hash_entry *), ptr);
+ r->number = 0;
+
+ unsigned int i;
+ for (i = 0; i < chains; i++)
+ r->chain[i] = NULL;
+
+ if (r->chain == NULL) {
+ alloc(r, 0, ptr);
+ return NULL;
+ }
+
+ return r;
+}
+
+/**
+ * Clone a hash table.
+ *
+ * \param ht Hash table to clone.
+ * \param alloc The allocator.
+ * \param pw The private data for the allocator.
+ * \param kf The function pointer used to copy the key.
+ * \param key_pw The private data for the key cloner.
+ * \param vf The function pointer used to copy the value.
+ * \param value_pw The private data for the value cloner.
+ *
+ * \return The cloned hash table.
+ */
+struct hash_table *hash_clone(struct hash_table *ht, dom_alloc alloc,
+ void *pw, key_func kf, void *key_pw, value_func vf, void *value_pw)
+{
+ struct hash_table *ret;
+
+ ret = hash_create(ht->nchains, ht->hash, alloc, pw);
+ if (ret == NULL)
+ return NULL;
+
+ void *key = NULL, *nkey = NULL;
+ void *value = NULL, *nvalue = NULL;
+ unsigned int c1, *c2 = NULL;
+ while ( (key = hash_iterate(ht, &c1, &c2)) != NULL) {
+ nkey = kf(key, key_pw, alloc, pw, true);
+ if (nkey == NULL) {
+ hash_destroy(ret, kf, key_pw, vf, value_pw);
+ return NULL;
+ }
+
+ value = hash_get(ht, key);
+ nvalue = vf(value, value_pw, alloc, pw, true);
+ if (nvalue == NULL) {
+ kf(nkey, key_pw, alloc, pw, false);
+ hash_destroy(ret, kf, key_pw, vf, value_pw);
+ return NULL;
+ }
+
+ if (hash_add(ret, nkey, nvalue, false) == false) {
+ hash_destroy(ret, kf, key_pw, vf, value_pw);
+ return NULL;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * Destroys a hash table, freeing all memory associated with it.
+ *
+ * \param ht Hash table to destroy. After the function returns, this
+ * will nolonger be valid.
+ * \param func The client provided destroy function
+ * \param pw The private data pointer for this destroy function
+ */
+
+void hash_destroy(struct hash_table *ht, key_func kf, void *key_pw,
+ value_func vf, void *value_pw)
+{
+ unsigned int i;
+
+ if (ht == NULL)
+ return;
+
+ assert(ht->alloc != NULL);
+
+ for (i = 0; i < ht->nchains; i++) {
+ if (ht->chain[i] != NULL) {
+ struct hash_entry *e = ht->chain[i];
+ while (e) {
+ struct hash_entry *n = e->next;
+ if (kf != NULL) {
+ kf(e->key, key_pw, ht->alloc, ht->ptr, false);
+ }
+ if (vf != NULL) {
+ vf(e->value, value_pw, ht->alloc, ht->ptr, false);
+ }
+ ht->alloc(e, 0, ht->ptr);
+ e = n;
+ }
+ }
+ }
+
+ ht->alloc(ht->chain, 0, ht->ptr);
+ ht->alloc(ht, 0, ht->ptr);
+}
+
+/**
+ * Adds a key/value pair to a hash table. If the key you're adding is already
+ * in the hash table, it does not replace it, but it does take precedent over
+ * it. The old key/value pair will be inaccessable but still in memory until
+ * hash_destroy() is called on the hash table.
+ *
+ * \param ht The hash table context to add the key/value pair to.
+ * \param key The key to associate the value with.
+ * \param value The value to associate the key with.
+ * \return true if the add succeeded, false otherwise. (Failure most likely
+ * indicates insufficent memory to make copies of the key and value.
+ */
+
+bool hash_add(struct hash_table *ht, void *key, void *value, bool replace)
+{
+ unsigned int h, c;
+ struct hash_entry *e;
+
+ if (ht == NULL || key == NULL || value == NULL)
+ return false;
+
+ h = ht->hash(key);
+ c = h % ht->nchains;
+
+ for (e = ht->chain[c]; e; e = e->next)
+ if (key == e->key) {
+ if (replace == true) {
+ e->value = value;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ assert(ht->alloc != NULL);
+
+ e = ht->alloc(NULL, sizeof(struct hash_entry), ht->ptr);
+ if (e == NULL) {
+ return false;
+ }
+
+ e->key = key;
+ e->value = value;
+
+ e->next = ht->chain[c];
+ ht->chain[c] = e;
+ ht->number ++;
+
+ return true;
+}
+
+/**
+ * Looks up a the value associated with with a key from a specific hash table.
+ *
+ * \param ht The hash table context to look up the key in.
+ * \param key The key to search for.
+ * \return The value associated with the key, or NULL if it was not found.
+ */
+
+void *hash_get(struct hash_table *ht, void *key)
+{
+ unsigned int h, c;
+ struct hash_entry *e;
+
+ if (ht == NULL || key == NULL)
+ return NULL;
+
+ h = ht->hash(key);
+ c = h % ht->nchains;
+
+ for (e = ht->chain[c]; e; e = e->next)
+ if (key == e->key)
+ return e->value;
+
+ return NULL;
+}
+
+/**
+ * Delete the key from the hashtable.
+ *
+ * \param ht The hashtable object
+ * \param key The key to delete
+ * \return The deleted value
+ */
+void *hash_del(struct hash_table *ht, void *key)
+{
+ unsigned int h, c;
+ struct hash_entry *e, *p;
+ void *ret;
+
+ if (ht == NULL || key == NULL)
+ return NULL;
+
+ h = ht->hash(key);
+ c = h % ht->nchains;
+
+ assert(ht->alloc != NULL);
+
+ p = ht->chain[c];
+ for (e = p; e; p = e, e = e->next)
+ if (key == e->key) {
+ if (p != e) {
+ p->next = e->next;
+ } else {
+ /* The first item in this chain is target*/
+ ht->chain[c] = e->next;
+ }
+
+ ret = e->value;
+ ht->alloc(e, 0, ht->ptr);
+ ht->number --;
+ return ret;
+ }
+
+ return NULL;
+}
+
+/**
+ * Iterate through all available hash keys.
+ *
+ * \param ht The hash table context to iterate.
+ * \param c1 Pointer to first context
+ * \param c2 Pointer to second context (set to 0 on first call)
+ * \return The next hash key, or NULL for no more keys
+ */
+
+void *hash_iterate(struct hash_table *ht, unsigned int *c1, unsigned int **c2) {
+ struct hash_entry **he = (struct hash_entry **)c2;
+
+ if (ht == NULL)
+ return NULL;
+
+ if (!*he)
+ *c1 = -1;
+ else
+ *he = (*he)->next;
+
+ if (*he)
+ return (*he)->key;
+
+ while (!*he) {
+ (*c1)++;
+ if (*c1 >= ht->nchains)
+ return NULL;
+ *he = ht->chain[*c1];
+ }
+ return (*he)->key;
+}
+
+/* Get the number of elements in this hash table
+ *
+ * \param ht The hash table
+ *
+ * \return The number of elements
+ */
+unsigned int hash_get_length(struct hash_table *ht)
+{
+ return ht->number;
+}
+
+/* Get the chain number of this hash table
+ *
+ * \param ht The hash table
+ *
+ * \return The number of chains
+ */
+unsigned int hash_get_chains(struct hash_table *ht)
+{
+ return ht->nchains;
+}
+
+/* Get the hash function of this hash table
+ *
+ * \param ht The hash table
+ *
+ * \return The hash function
+ */
+hash_func hash_get_func(struct hash_table *ht)
+{
+ return ht->hash;
+}
+
+/* A simple test rig. To compile, use:
+ * gcc -g -o hashtest -I../ -I../../include -DTEST_RIG hashtable.c
+ *
+ * If you make changes to this hash table implementation, please rerun this
+ * test, and if possible, through valgrind to make sure there are no memory
+ * leaks or invalid memory accesses. If you add new functionality, please
+ * include a test for it that has good coverage along side the other tests.
+ */
+
+#ifdef TEST_RIG
+
+
+/**
+ * Hash a pointer, returning a 32bit value.
+ *
+ * \param ptr The pointer to hash.
+ * \return The calculated hash value for the pointer.
+ */
+
+static inline unsigned int hash_pointer_fnv(void *ptr)
+{
+ return (unsigned int) ptr;
+}
+
+static void *test_alloc(void *p, size_t size, void *ptr)
+{
+ if (p != NULL) {
+ free(p);
+ return NULL;
+ }
+
+ if (p == NULL) {
+ return malloc(size);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ struct hash_table *a, *b;
+ FILE *dict;
+ char keybuf[BUFSIZ], valbuf[BUFSIZ];
+ int i;
+ char *cow="cow", *moo="moo", *pig="pig", *oink="oink",
+ *chicken="chikcken", *cluck="cluck",
+ *dog="dog", *woof="woof", *cat="cat",
+ *meow="meow";
+ void *ret;
+
+ a = hash_create(79, hash_pointer_fnv, test_alloc, NULL);
+ assert(a != NULL);
+
+ b = hash_create(103, hash_pointer_fnv, test_alloc, NULL);
+ assert(b != NULL);
+
+ hash_add(a, cow, moo ,true);
+ hash_add(b, moo, cow ,true);
+
+ hash_add(a, pig, oink ,true);
+ hash_add(b, oink, pig ,true);
+
+ hash_add(a, chicken, cluck ,true);
+ hash_add(b, cluck, chicken ,true);
+
+ hash_add(a, dog, woof ,true);
+ hash_add(b, woof, dog ,true);
+
+ hash_add(a, cat, meow ,true);
+ hash_add(b, meow, cat ,true);
+
+#define MATCH(x,y) assert(!strcmp((char *)hash_get(a, x), (char *)y)); \
+ assert(!strcmp((char *)hash_get(b, y), (char *)x))
+ MATCH(cow, moo);
+ MATCH(pig, oink);
+ MATCH(chicken, cluck);
+ MATCH(dog, woof);
+ MATCH(cat, meow);
+
+ assert(hash_get_length(a) == 5);
+ assert(hash_get_length(b) == 5);
+
+ hash_del(a, cat);
+ hash_del(b, meow);
+ assert(hash_get(a, cat) == NULL);
+ assert(hash_get(b, meow) == NULL);
+
+ assert(hash_get_length(a) == 4);
+ assert(hash_get_length(b) == 4);
+
+ hash_destroy(a, NULL, NULL);
+ hash_destroy(b, NULL, NULL);
+
+ /* this test requires /usr/share/dict/words - a large list of English
+ * words. We load the entire file - odd lines are used as keys, and
+ * even lines are used as the values for the previous line. we then
+ * work through it again making sure everything matches.
+ *
+ * We do this twice - once in a hash table with many chains, and once
+ * with a hash table with fewer chains.
+ */
+
+ a = hash_create(1031, hash_pointer_fnv, test_alloc, NULL);
+ b = hash_create(7919, hash_pointer_fnv, test_alloc, NULL);
+
+ dict = fopen("/usr/share/dict/words", "r");
+ if (dict == NULL) {
+ fprintf(stderr, "Unable to open /usr/share/dict/words - extensive testing skipped.\n");
+ exit(0);
+ }
+
+ while (!feof(dict)) {
+ fscanf(dict, "%s", keybuf);
+ fscanf(dict, "%s", valbuf);
+ hash_add(a, keybuf, valbuf, true);
+ hash_add(b, keybuf, valbuf, true);
+ }
+
+ for (i = 0; i < 5; i++) {
+ fseek(dict, 0, SEEK_SET);
+
+ while (!feof(dict)) {
+ fscanf(dict, "%s", keybuf);
+ fscanf(dict, "%s", valbuf);
+ assert(strcmp(hash_get(a, keybuf), valbuf) == 0);
+ assert(strcmp(hash_get(b, keybuf), valbuf) == 0);
+ }
+ }
+
+ hash_destroy(a, NULL, NULL);
+ hash_destroy(b, NULL, NULL);
+
+ fclose(dict);
+
+ return 0;
+}
+
+#endif
Index: src/utils/resource_mgr.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ src/utils/resource_mgr.c 2009-07-30 11:28:53.000000000 +0100
@@ -0,0 +1,97 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ */
+
+#include "resource_mgr.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include <libwapcaplet/libwapcaplet.h>
+#include "core/string.h"
+
+/**
+ * Allocate some memory with this allocator
+ *
+ * \param res The resource manager
+ * \param size The size of memory to allocate
+ */
+void *resource_mgr_alloc(struct resource_mgr *res, void *ptr, size_t size)
+{
+ return res->alloc(ptr, size, res->pw);
+}
+
+/**
+ * Create a dom_string using this resource manager
+ *
+ * \param res The resource manager
+ * \param data The data pointer
+ * \param len The length of data
+ * \param result The returned dom_string
+ */
+dom_exception resource_mgr_create_string(struct resource_mgr *res,
+ const uint8_t *data, size_t len, struct dom_string **result)
+{
+ return dom_string_create(res->alloc, res->pw, data, len, result);
+}
+
+/**
+ * Create a lwc_string using this resource manager
+ *
+ * \param res The resource manager
+ * \param data The data pointer
+ * \param len The length of the data
+ * \param result The returned lwc_string
+ */
+dom_exception resource_mgr_create_lwcstring(struct resource_mgr *res,
+ const uint8_t *data, size_t len, struct lwc_string_s **result)
+{
+ lwc_error lerr;
+
+ assert(res->ctx != NULL);
+
+ lerr = lwc_context_intern(res->ctx, (const char *) data, len,
+ result);
+
+ return dom_exception_from_lwc_error(lerr);
+}
+
+/**
+ * Create a lwc_string from a dom_string using this resource manager
+ *
+ * \param res The resource manager
+ * \param str The dom_string to intern
+ * \param result The returned lwc_string
+ */
+dom_exception resource_mgr_create_string_from_lwcstring(struct resource_mgr *res,
+ struct lwc_string_s *str, struct dom_string **result)
+{
+ assert(res->ctx != NULL);
+
+ return dom_string_create_from_lwcstring(res->alloc, res->pw, res->ctx,
+ str, result);
+}
+
+/**
+ * Create a hash_table using this resource manager
+ *
+ * \param res The resource manager
+ * \param chains The number of buckets of the hash table
+ * \param f The hash function
+ * \param ht The returned hash table
+ */
+dom_exception resource_mgr_create_hashtable(struct resource_mgr *res,
+ size_t chains, hash_func f, struct hash_table **ht)
+{
+ struct hash_table *ret;
+
+ ret = hash_create(chains, f, res->alloc, res->pw);
+ if (ret == NULL)
+ return DOM_NO_MEM_ERR;
+
+ *ht = ret;
+ return DOM_NO_ERR;
+}
Index: src/core/string.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ src/core/string.h 2009-07-30 11:28:53.000000000 +0100
@@ -0,0 +1,28 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ */
+
+#ifndef dom_internal_core_string_h_
+#define dom_internal_core_string_h_
+
+#include <dom/core/string.h>
+
+/* Create a DOM string from a lwc_string
+ * This function call mainly used for create a string from lwc_string */
+dom_exception dom_string_create_from_lwcstring(dom_alloc alloc, void *pw,
+ struct lwc_context_s *ctx, struct lwc_string_s *str,
+ struct dom_string **ret);
+
+/* Make the dom_string be interned in the lwc_context_s */
+dom_exception dom_string_intern(struct dom_string *str,
+ struct lwc_context_s *ctx, struct lwc_string_s **lwcstr);
+
+/* Compare the raw data of two lwc_strings for equality when the two string
+ * belong to different lwc_context */
+int dom_lwc_string_compare_raw(struct lwc_string_s *s1, struct lwc_string_s *s2);
+
+#endif
+
Index: src/core/typeinfo.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ src/core/typeinfo.c 2009-07-30 11:28:55.000000000 +0100
@@ -0,0 +1,68 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ */
+
+#include <dom/core/typeinfo.h>
+#include <dom/core/string.h>
+
+#include "utils/utils.h"
+
+/* TypeInfo object */
+struct dom_type_info {
+ struct lwc_string_s *type;
+ struct lwc_string_s *namespace;
+};
+
+/**
+ * Get the type name of this dom_type_info
+ *
+ * \param ti The dom_type_info
+ * \param ret The name
+ */
+dom_exception _dom_type_info_get_type_name(dom_type_info *ti,
+ struct dom_string **ret)
+{
+ UNUSED(ti);
+ UNUSED(ret);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Get the namespace of this type info
+ *
+ * \param ti The dom_type_info
+ * \param ret The namespace
+ */
+dom_exception _dom_type_info_get_type_namespace(dom_type_info *ti,
+ struct dom_string **ret)
+{
+ UNUSED(ti);
+ UNUSED(ret);
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Whether this type info is derived from another one
+ *
+ * \param ti The dom_type_info
+ * \param namespace The namespace of name
+ * \param name The name of the base typeinfo
+ * \param method The derive method
+ * \param ret The return value
+ */
+dom_exception _dom_type_info_is_derived(dom_type_info *ti,
+ struct dom_string *namespace, struct dom_string *name,
+ dom_type_info_derivation_method method, bool *ret)
+{
+ UNUSED(ti);
+ UNUSED(namespace);
+ UNUSED(name);
+ UNUSED(method);
+ UNUSED(ret);
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
Index: src/bootstrap/implementation.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ src/bootstrap/implementation.c 2009-07-30 11:29:00.000000000 +0100
@@ -0,0 +1,422 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2007 John-Mark Bell <jmb(a)netsurf-browser.org>
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ */
+
+/**
+ * Note: The DOMImplementation Object here is a singleton object. It is
+ * initialised when the libDOM is initialised, it registers itself into
+ * the implreg and clients of it can get it by calling:
+ *
+ * dom_implregistry_get_dom_implementation or
+ * dom_implregistry_get_dom_implementation_list
+ *
+ */
+
+#include <dom/bootstrap/implpriv.h>
+#include <dom/bootstrap/implregistry.h>
+#include <dom/dom.h>
+
+#include <libwapcaplet/libwapcaplet.h>
+
+#include "core/node.h"
+#include "core/document_type.h"
+
+#include "utils/utils.h"
+#include "utils/validate.h"
+#include "utils/namespace.h"
+
+#include "bootstrap/implementation.h"
+
+static dom_alloc _alloc;
+static void *_pw;
+
+static dom_exception impl_get_dom_implementation(
+ struct dom_string *features,
+ struct dom_implementation **impl);
+static dom_exception impl_get_dom_implementation_list(
+ struct dom_string *features,
+ struct dom_implementation_list **list);
+
+static dom_exception impl_implementation_has_feature(
+ struct dom_implementation *impl,
+ struct dom_string *feature,
+ struct dom_string *version,
+ bool *result);
+static dom_exception impl_implementation_create_document_type(
+ struct dom_implementation *impl,
+ struct dom_string *qname,
+ struct dom_string *public_id,
+ struct dom_string *system_id,
+ dom_alloc alloc, void *pw, struct lwc_context_s *ctx,
+ struct dom_document_type **doctype);
+static dom_exception impl_implementation_create_document(
+ struct dom_implementation *impl,
+ struct dom_string *namespace,
+ struct dom_string *qname,
+ struct dom_document_type *doctype,
+ dom_alloc alloc, void *pw, struct lwc_context_s *ctx,
+ struct dom_document **doc);
+static dom_exception impl_implementation_get_feature(
+ struct dom_implementation *impl,
+ struct dom_string *feature,
+ struct dom_string *version,
+ void **object);
+static void dom_implementation_destroy(struct dom_implementation *impl);
+
+
+static struct dom_implementation_source dom_impl_src = {
+ impl_get_dom_implementation,
+ impl_get_dom_implementation_list
+};
+
+static struct dom_implementation dom_impl = {
+ impl_implementation_has_feature,
+ impl_implementation_create_document_type,
+ impl_implementation_create_document,
+ impl_implementation_get_feature,
+ dom_implementation_destroy,
+ 0
+};
+
+/**
+ * Get a DOM implementation that supports the requested features
+ *
+ * \param features String containing required features
+ * \param impl Pointer to location to receive implementation
+ * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion
+ *
+ * Any memory allocated by this call should be allocated using
+ * the provided memory (de)allocation function. The implementation's
+ * destroy() method will be called once it is no longer used.
+ *
+ * The implementation will be referenced, so the client need not
+ * do this explicitly. The client must unref the implementation
+ * once it has finished with it.
+ */
+dom_exception impl_get_dom_implementation(
+ struct dom_string *features,
+ struct dom_implementation **impl)
+{
+ UNUSED(features);
+
+ dom_impl.refcnt++;
+
+ *impl = &dom_impl;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Get a list of DOM implementations that support the requested
+ * features
+ *
+ * \param features String containing required features
+ * \param list Pointer to location to receive list
+ * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion
+ *
+ * Any memory allocated by this call should be allocated using
+ * the provided memory (de)allocation function. The ::alloc/::pw
+ * pair must be stored on the list object, such that the list
+ * and its contents may be freed once they are no longer needed.
+ *
+ * List nodes reference the implementation objects they point to.
+ *
+ * The list will be referenced, so the client need not do this
+ * explicitly. The client must unref the list once it has finished
+ * with it.
+ */
+dom_exception impl_get_dom_implementation_list(
+ struct dom_string *features,
+ struct dom_implementation_list **list)
+{
+ struct dom_implementation_list *l;
+ struct dom_implementation_list_item *i;
+
+ UNUSED(features);
+
+ l = _alloc(NULL, sizeof(struct dom_implementation_list), _pw);
+ if (l == NULL)
+ return DOM_NO_MEM_ERR;
+
+ i = _alloc(NULL, sizeof(struct dom_implementation_list_item), _pw);
+ if (i == NULL) {
+ _alloc(l, 0, _pw);
+ return DOM_NO_MEM_ERR;
+ }
+
+ i->impl = &dom_impl;
+ i->next = NULL;
+ i->prev = NULL;
+
+ l->head = i;
+
+ l->refcnt = 1;
+
+ *list = l;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Test whether a DOM implementation implements a specific feature
+ * and version
+ *
+ * \param impl The DOM implementation to query
+ * \param feature The feature to test for
+ * \param version The version number of the feature to test for
+ * \param result Pointer to location to receive result
+ * \return DOM_NO_ERR.
+ */
+dom_exception impl_implementation_has_feature(
+ struct dom_implementation *impl,
+ struct dom_string *feature,
+ struct dom_string *version,
+ bool *result)
+{
+ UNUSED(impl);
+ UNUSED(feature);
+ UNUSED(version);
+ UNUSED(result);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Create a document type node
+ *
+ * \param impl The implementation to create the node
+ * \param qname The qualified name of the document type
+ * \param public_id The external subset public identifier
+ * \param system_id The external subset system identifier
+ * \param doctype Pointer to location to receive result
+ * \return DOM_NO_ERR on success,
+ * DOM_INVALID_CHARACTER_ERR if ::qname is invalid,
+ * DOM_NAMESPACE_ERR if ::qname is malformed.
+ *
+ * Any memory allocated by this call should be allocated using
+ * the provided memory (de)allocation function.
+ *
+ * The doctype will be referenced, so the client need not do this
+ * explicitly. The client must unref the doctype once it has
+ * finished with it.
+ */
+dom_exception impl_implementation_create_document_type(
+ struct dom_implementation *impl,
+ struct dom_string *qname,
+ struct dom_string *public_id,
+ struct dom_string *system_id,
+ dom_alloc alloc, void *pw, struct lwc_context_s *ctx,
+ struct dom_document_type **doctype)
+{
+ struct dom_document_type *d;
+ struct dom_string *prefix = NULL, *lname = NULL;
+ dom_exception err;
+
+ UNUSED(impl);
+
+ if (qname != NULL && _dom_validate_name(qname) == false)
+ return DOM_INVALID_CHARACTER_ERR;
+
+ err = _dom_namespace_split_qname(qname, &prefix, &lname);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ if ((prefix != NULL && _dom_validate_ncname(prefix) == false) ||
+ (lname != NULL && _dom_validate_ncname(lname) == false))
+ return DOM_NAMESPACE_ERR;
+
+ /* Create the doctype */
+ err = dom_document_type_create(qname, public_id, system_id,
+ alloc, pw, ctx, &d);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ *doctype = d;
+ if (prefix != NULL)
+ dom_string_unref(prefix);
+ if (lname != NULL)
+ dom_string_unref(lname);
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Create a document node
+ *
+ * \param impl The implementation to create the node
+ * \param namespace The namespace URI of the document element
+ * \param qname The qualified name of the document element
+ * \param doctype The type of document to create
+ * \param doc Pointer to location to receive result
+ * \return DOM_NO_ERR on success,
+ * DOM_INVALID_CHARACTER_ERR if ::qname is invalid,
+ * DOM_NAMESPACE_ERR if ::qname is malformed, or if
+ * ::qname has a prefix and
+ * ::namespace is NULL, or if
+ * ::qname is NULL and ::namespace
+ * is non-NULL, or if ::qname has
+ * a prefix "xml" and ::namespace
+ * is not
+ * "http://www.w3.org/XML/1998/namespace ",
+ * or if ::impl does not support
+ * the "XML" feature and
+ * ::namespace is non-NULL,
+ * DOM_WRONG_DOCUMENT_ERR if ::doctype is already being
+ * used by a document, or if it
+ * was not created by ::impl,
+ * DOM_NOT_SUPPORTED_ERR if ::impl does not support the
+ * feature "XML" and the language
+ * exposed through Document does
+ * not support XML namespaces.
+ *
+ * Any memory allocated by this call should be allocated using
+ * the provided memory (de)allocation function.
+ *
+ * The document will be referenced, so the client need not do this
+ * explicitly. The client must unref the document once it has
+ * finished with it.
+ */
+dom_exception impl_implementation_create_document(
+ struct dom_implementation *impl,
+ struct dom_string *namespace,
+ struct dom_string *qname,
+ struct dom_document_type *doctype,
+ dom_alloc alloc, void *pw, struct lwc_context_s *ctx,
+ struct dom_document **doc)
+{
+ struct dom_document *d;
+ dom_exception err;
+
+ if (qname != NULL && _dom_validate_name(qname) == false)
+ return DOM_INVALID_CHARACTER_ERR;
+
+ err = _dom_namespace_validate_qname(qname, namespace);
+ if (err != DOM_NO_ERR)
+ return DOM_NAMESPACE_ERR;
+
+ if (doctype != NULL) {
+ if (dom_node_get_parent(doctype) != NULL ||
+ dom_document_type_get_impl(doctype) != impl)
+ return DOM_WRONG_DOCUMENT_ERR;
+ }
+
+ /* Create document object */
+ err = dom_document_create(impl, alloc, pw, ctx, &d);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ /* Set its doctype, if necessary */
+ if (doctype != NULL) {
+ struct dom_node *ins_doctype = NULL;
+
+ err = dom_node_append_child((struct dom_node *) d,
+ (struct dom_node *) doctype, &ins_doctype);
+ if (err != DOM_NO_ERR) {
+ dom_node_unref((struct dom_node *) d);
+ return err;
+ }
+
+ /* Not interested in inserted doctype */
+ if (ins_doctype != NULL)
+ dom_node_unref(ins_doctype);
+ }
+
+ /* Create root element and attach it to document */
+ if (qname != NULL) {
+ struct dom_element *e;
+ struct dom_node *inserted;
+
+ err = dom_document_create_element_ns(d, namespace, qname, &e);
+ if (err != DOM_NO_ERR) {
+ dom_node_unref((struct dom_node *) d);
+ return err;
+ }
+
+ err = dom_node_append_child((struct dom_node *) d,
+ (struct dom_node *) e, &inserted);
+ if (err != DOM_NO_ERR) {
+ dom_node_unref((struct dom_node *) e);
+ dom_node_unref((struct dom_node *) d);
+ return err;
+ }
+
+ /* No longer interested in inserted node */
+ dom_node_unref(inserted);
+
+ /* Done with element */
+ dom_node_unref((struct dom_node *) e);
+ }
+
+ *doc = d;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Retrieve a specialized object which implements the specified
+ * feature and version
+ *
+ * \param impl The implementation to create the object
+ * \param feature The requested feature
+ * \param version The version number of the feature
+ * \param object Pointer to location to receive object
+ * \return DOM_NO_ERR.
+ *
+ * Any memory allocated by this call should be allocated using
+ * the provided memory (de)allocation function.
+ */
+dom_exception impl_implementation_get_feature(
+ struct dom_implementation *impl,
+ struct dom_string *feature,
+ struct dom_string *version,
+ void **object)
+{
+ UNUSED(impl);
+ UNUSED(feature);
+ UNUSED(version);
+ UNUSED(object);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Destroy a DOM implementation instance
+ *
+ * \param impl The instance to destroy
+ */
+void dom_implementation_destroy(struct dom_implementation *impl)
+{
+ UNUSED(impl);
+
+ /* Nothing to do -- we're statically allocated */
+}
+
+/**
+ * Initialise the DOM implementation
+ *
+ * \param alloc Pointer to memory (de)allocation function
+ * \param pw Pointer to client-specific private data
+ * \return DOM_NO_ERR on success
+ */
+dom_exception dom_implementation_initialise(dom_alloc alloc, void *pw)
+{
+ _alloc = alloc;
+ _pw = pw;
+
+ return dom_register_source(&dom_impl_src);
+}
+
+/**
+ * Finalise the DOM implementation
+ */
+void dom_implementation_finalise(void)
+{
+ _alloc = NULL;
+ _pw = NULL;
+}
+
+
Index: src/bootstrap/implementation.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ src/bootstrap/implementation.h 2009-07-30 11:29:00.000000000 +0100
@@ -0,0 +1,14 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
+ */
+
+#ifndef dom_bootstrap_implementation_h_
+#define dom_bootstrap_implementation_h_
+
+dom_exception dom_implementation_initialise(dom_alloc alloc, void *pw);
+void dom_implementation_finalise(void);
+
+#endif
Changed files
Makefile | 3
bindings/hubbub/parser.c | 108 +
bindings/hubbub/parser.h | 4
bindings/xml/Makefile | 2
bindings/xml/xmlparser.c | 75 -
bindings/xml/xmlparser.h | 4
include/dom/bootstrap/implpriv.h | 42
include/dom/bootstrap/implregistry.h | 10
include/dom/core/attr.h | 8
include/dom/core/document.h | 6
include/dom/core/element.h | 2
include/dom/core/implementation.h | 11
include/dom/core/namednodemap.h | 4
include/dom/core/node.h | 18
include/dom/core/nodelist.h | 4
include/dom/core/string.h | 17
include/dom/dom.h | 18
src/bootstrap/Makefile | 2
src/bootstrap/implregistry.c | 60 -
src/bootstrap/init_fini.c | 19
src/core/Makefile | 13
src/core/attr.c | 296 ++++
src/core/attr.h | 94 +
src/core/cdatasection.c | 53
src/core/cdatasection.h | 21
src/core/characterdata.c | 62 -
src/core/characterdata.h | 23
src/core/comment.c | 48
src/core/comment.h | 19
src/core/doc_fragment.c | 81 -
src/core/doc_fragment.h | 20
src/core/document.c | 856 +++++++++++---
src/core/document.h | 102 +
src/core/document_type.c | 193 ++-
src/core/document_type.h | 26
src/core/element.c | 2077 +++++++++++++++++++++++++----------
src/core/element.h | 96 +
src/core/entity_ref.c | 77 -
src/core/entity_ref.h | 21
src/core/implementation.c | 25
src/core/impllist.c | 18
src/core/namednodemap.c | 350 -----
src/core/namednodemap.h | 48
src/core/node.c | 1089 +++++++++++++++---
src/core/node.h | 128 +-
src/core/nodelist.c | 193 ++-
src/core/nodelist.h | 19
src/core/pi.c | 49
src/core/pi.h | 18
src/core/string.c | 336 +++++
src/core/text.c | 289 ++++
src/core/text.h | 44
src/utils/Makefile | 3
src/utils/namespace.c | 97 +
src/utils/namespace.h | 7
test/Makefile | 13
56 files changed, 5455 insertions(+), 1866 deletions(-)
Index: test/Makefile
===================================================================
--- test/Makefile (revision 8909)
+++ test/Makefile (working copy)
@@ -1,18 +1,17 @@
-testlib_files := lib/comparators.c;lib/list.c;lib/testassert.c
-testlib_files := $(testlib_files);lib/testobject.c;lib/utils.c
+testutils_files := testutils/comparators.c;testutils/list.c;testutils/domasserts.c
+testutils_files := $(testutils_files);testutils/utils.c;testutils/foreach.c;testutils/load.c
-TESTCFLAGS := $(TESTCFLAGS) -I$(CURDIR) -I$(CURDIR)/$(DIR)
+TESTCFLAGS := $(TESTCFLAGS) -I$(CURDIR) -I$(CURDIR)/testutils
-DIR_TEST_ITEMS := binding:binding.c;$(testlib_files) \
- test-list:test-list.c;$(testlib_files)
+DIR_TEST_ITEMS := $(testutils_files)
define add_xml_test
ifeq ($$(WANT_TEST),yes)
- $(DIR)xml/c/$1.c: $(DIR)xml/tests/$1.xml
+ $(DIR)xml/c/$1.c: $(DIR)xml/tests/$1.xml
$$(Q)$$(XSLTPROC) $$(XSLTPROCFLAGS) -o $$@ \
$(DIR)transform/test-to-c.xsl $$<
- DIR_TEST_ITEMS := $$(DIR_TEST_ITEMS) $1:xml/c/$1.c;$$(testlib_files)
+ DIR_TEST_ITEMS := $$(DIR_TEST_ITEMS) $1:xml/c/$1.c;$$(testlib_files)
endif
DISTCLEAN_ITEMS := $$(DISTCLEAN_ITEMS) $(DIR)xml/c/$1.c
Index: include/dom/dom.h
===================================================================
--- include/dom/dom.h (revision 8909)
+++ include/dom/dom.h (working copy)
@@ -38,17 +38,19 @@
#include <dom/core/string.h>
#include <dom/core/text.h>
#include <dom/core/pi.h>
+#include <dom/core/typeinfo.h>
+#include <dom/core/comment.h>
typedef enum dom_namespace {
- DOM_NAMESPACE_NULL = 0,
- DOM_NAMESPACE_HTML = 1,
- DOM_NAMESPACE_MATHML = 2,
- DOM_NAMESPACE_SVG = 3,
- DOM_NAMESPACE_XLINK = 4,
- DOM_NAMESPACE_XML = 5,
- DOM_NAMESPACE_XMLNS = 6,
+ DOM_NAMESPACE_NULL = 0,
+ DOM_NAMESPACE_HTML = 1,
+ DOM_NAMESPACE_MATHML = 2,
+ DOM_NAMESPACE_SVG = 3,
+ DOM_NAMESPACE_XLINK = 4,
+ DOM_NAMESPACE_XML = 5,
+ DOM_NAMESPACE_XMLNS = 6,
- DOM_NAMESPACE_COUNT = 7
+ DOM_NAMESPACE_COUNT = 7
} dom_namespace;
extern struct dom_string *dom_namespaces[DOM_NAMESPACE_COUNT];
Index: include/dom/core/element.h
===================================================================
--- include/dom/core/element.h (revision 8909)
+++ include/dom/core/element.h (working copy)
@@ -211,7 +211,7 @@
dom_element_remove_attribute_ns(element, namespace,
localname);
}
-#define dom_element_remove_attribute_ns(e, n, l, v) \
+#define dom_element_remove_attribute_ns(e, n, l) \
dom_element_remove_attribute_ns((dom_element *) (e), \
(struct dom_string *) (n), (struct dom_string *) (l))
Index: include/dom/core/string.h
===================================================================
--- include/dom/core/string.h (revision 8909)
+++ include/dom/core/string.h (working copy)
@@ -10,6 +10,7 @@
#include <inttypes.h>
#include <stddef.h>
+#include <libwapcaplet/libwapcaplet.h>
#include <dom/functypes.h>
#include <dom/core/exceptions.h>
@@ -25,6 +26,16 @@
dom_exception dom_string_create(dom_alloc alloc, void *pw,
const uint8_t *ptr, size_t len, struct dom_string **str);
+/* Clone a dom_string */
+dom_exception dom_string_clone(dom_alloc alloc, void *pw, struct dom_string *str,
+ struct dom_string **ret);
+
+/* Get the internal lwc_string */
+dom_exception dom_string_get_intern(struct dom_string *str,
+ struct lwc_context_s **ctx, struct lwc_string_s **lwcstr);
+/* Map the lwc_error to dom_exception */
+dom_exception dom_exception_from_lwc_error(lwc_error err);
+
/* Case sensitively compare two DOM strings */
int dom_string_cmp(struct dom_string *s1, struct dom_string *s2);
/* Case insensitively compare two DOM strings */
@@ -38,6 +49,12 @@
/* Get the length, in characters, of a dom string */
uint32_t dom_string_length(struct dom_string *str);
+/* Get the UCS-4 character at position index, the index should be in
+ * [0, length), and length can be get by calling dom_string_length
+ */
+dom_exception dom_string_at(struct dom_string *str, uint32_t index,
+ uint32_t *ch);
+
/* Concatenate two dom strings */
dom_exception dom_string_concat(struct dom_string *s1, struct dom_string *s2,
struct dom_string **result);
Index: include/dom/core/attr.h
===================================================================
--- include/dom/core/attr.h (revision 8909)
+++ include/dom/core/attr.h (working copy)
@@ -32,7 +32,7 @@
struct dom_string **result);
dom_exception (*dom_attr_set_value)(struct dom_attr *attr,
struct dom_string *value);
- dom_exception (*dom_attr_get_owner)(struct dom_attr *attr,
+ dom_exception (*dom_attr_get_owner_element)(struct dom_attr *attr,
struct dom_element **result);
dom_exception (*dom_attr_get_schema_type_info)(struct dom_attr *attr,
struct dom_type_info **result);
@@ -74,13 +74,13 @@
#define dom_attr_set_value(a, v) dom_attr_set_value((struct dom_attr *) (a), \
(struct dom_string *) (v))
-static inline dom_exception dom_attr_get_owner(struct dom_attr *attr,
+static inline dom_exception dom_attr_get_owner_element(struct dom_attr *attr,
struct dom_element **result)
{
return ((dom_attr_vtable *) ((dom_node *) attr)->vtable)->
- dom_attr_get_owner(attr, result);
+ dom_attr_get_owner_element(attr, result);
}
-#define dom_attr_get_owner(a, r) dom_attr_get_owner((struct dom_attr *) (a), \
+#define dom_attr_get_owner_element(a, r) dom_attr_get_owner_element((struct dom_attr *) (a), \
(struct dom_element **) (r))
static inline dom_exception dom_attr_get_schema_type_info(struct dom_attr *attr,
Index: include/dom/core/document.h
===================================================================
--- include/dom/core/document.h (revision 8909)
+++ include/dom/core/document.h (working copy)
@@ -9,6 +9,8 @@
#define dom_core_document_h_
#include <stdbool.h>
+#include <inttypes.h>
+#include <stddef.h>
#include <stdint.h>
#include <dom/core/exceptions.h>
@@ -29,6 +31,7 @@
struct dom_processing_instruction;
struct dom_string;
struct dom_text;
+struct lwc_string_s;
typedef struct dom_document dom_document;
@@ -114,9 +117,6 @@
struct dom_string *qname, struct dom_node **result);
} dom_document_vtable;
-dom_exception dom_document_create_string(struct dom_document *doc,
- const uint8_t *data, size_t len, struct dom_string **result);
-
static inline dom_exception dom_document_get_doctype(struct dom_document *doc,
struct dom_document_type **result)
{
Index: include/dom/core/nodelist.h
===================================================================
--- include/dom/core/nodelist.h (revision 8909)
+++ include/dom/core/nodelist.h (working copy)
@@ -25,4 +25,8 @@
#define dom_nodelist_item(l, i, n) _dom_nodelist_item((dom_nodelist *) (l), \
(unsigned long) (i), (dom_node **) (n))
+bool _dom_nodelist_equal(struct dom_nodelist *l1, struct dom_nodelist *l2);
+#define dom_nodelist_equal(l1, l2) _dom_nodelist_equal( \
+ (struct dom_nodelist *) (l1), (struct dom_nodelist *) (l2))
+
#endif
Index: include/dom/core/implementation.h
===================================================================
--- include/dom/core/implementation.h (revision 8909)
+++ include/dom/core/implementation.h (working copy)
@@ -30,20 +30,19 @@
dom_exception dom_implementation_create_document_type(
struct dom_implementation *impl, struct dom_string *qname,
struct dom_string *public_id, struct dom_string *system_id,
- struct dom_document_type **doctype,
- dom_alloc alloc, void *pw);
+ dom_alloc alloc, void *pw, struct lwc_context_s *ctx,
+ struct dom_document_type **doctype);
dom_exception dom_implementation_create_document(
struct dom_implementation *impl,
struct dom_string *namespace, struct dom_string *qname,
struct dom_document_type *doctype,
- struct dom_document **doc,
- dom_alloc alloc, void *pw);
+ dom_alloc alloc, void *pw, struct lwc_context_s *ctx,
+ struct dom_document **doc);
dom_exception dom_implementation_get_feature(
struct dom_implementation *impl,
struct dom_string *feature, struct dom_string *version,
- void **object,
- dom_alloc alloc, void *pw);
+ void **object);
#endif
Index: include/dom/core/node.h
===================================================================
--- include/dom/core/node.h (revision 8909)
+++ include/dom/core/node.h (working copy)
@@ -127,7 +127,7 @@
dom_node_internal **result);
dom_exception (*dom_node_normalize)(dom_node_internal *node);
dom_exception (*dom_node_is_supported)(dom_node_internal *node,
- struct dom_string *feature, dom_node_internal *version,
+ struct dom_string *feature, struct dom_string *version,
bool *result);
dom_exception (*dom_node_get_namespace)(dom_node_internal *node,
struct dom_string **result);
@@ -367,16 +367,16 @@
#define dom_node_normalize(n) dom_node_normalize((dom_node *) (n))
static inline dom_exception dom_node_is_supported(struct dom_node *node,
- struct dom_string *feature, struct dom_node *version,
+ struct dom_string *feature, struct dom_string *version,
bool *result)
{
return ((dom_node_vtable *) node->vtable)->dom_node_is_supported(
(dom_node_internal *) node, feature,
- (dom_node_internal *) version, result);
+ version, result);
}
#define dom_node_is_supported(n, f, v, r) dom_node_is_supported( \
- (dom_node *) (n), (struct dom_string *) (f), (dom_node *) (v),\
- (bool *) (r))
+ (dom_node *) (n), (struct dom_string *) (f), \
+ (struct dom_string *) (v), (bool *) (r))
static inline dom_exception dom_node_get_namespace(struct dom_node *node,
struct dom_string **result)
@@ -385,7 +385,7 @@
(dom_node_internal *) node, result);
}
#define dom_node_get_namespace(n, r) dom_node_get_namespace((dom_node *) (n), \
- (struct dom_string *) (r))
+ (struct dom_string **) (r))
static inline dom_exception dom_node_get_prefix(struct dom_node *node,
struct dom_string **result)
@@ -394,7 +394,7 @@
(dom_node_internal *) node, result);
}
#define dom_node_get_prefix(n, r) dom_node_get_prefix((dom_node *) (n), \
- (struct dom_string *) (r))
+ (struct dom_string **) (r))
static inline dom_exception dom_node_set_prefix(struct dom_node *node,
struct dom_string *prefix)
@@ -412,7 +412,7 @@
(dom_node_internal *) node, result);
}
#define dom_node_get_local_name(n, r) dom_node_get_local_name((dom_node *) (n),\
- (struct dom_string *) (r))
+ (struct dom_string **) (r))
static inline dom_exception dom_node_has_attributes(struct dom_node *node,
bool *result)
@@ -489,7 +489,7 @@
return ((dom_node_vtable *) node->vtable)->dom_node_is_default_namespace(
(dom_node_internal *) node, namespace, result);
}
-#define dom_node_is_default_namesapce(n, ns, r) dom_node_is_default_namespace(\
+#define dom_node_is_default_namespace(n, ns, r) dom_node_is_default_namespace(\
(dom_node *) (n), (struct dom_string *) (ns), (bool *) (r))
static inline dom_exception dom_node_lookup_namespace(struct dom_node *node,
Index: include/dom/core/namednodemap.h
===================================================================
--- include/dom/core/namednodemap.h (revision 8909)
+++ include/dom/core/namednodemap.h (working copy)
@@ -43,7 +43,7 @@
#define dom_namednodemap_remove_named_item(m, n, r) \
_dom_namednodemap_remove_named_item((dom_namednodemap *) (m), \
- (dom_string *) (n), (dom_node **) (n))
+ (dom_string *) (n), (dom_node **) (r))
dom_exception _dom_namednodemap_item(struct dom_namednodemap *map,
@@ -60,7 +60,7 @@
#define dom_namednodemap_get_named_item_ns(m, n, l, r) \
_dom_namednodemap_get_named_item_ns((dom_namednodemap *) (m), \
- (dom_string *) (n), (dom_string *) (l), (dom_node **) (n))
+ (dom_string *) (n), (dom_string *) (l), (dom_node **) (r))
dom_exception _dom_namednodemap_set_named_item_ns(
Index: include/dom/bootstrap/implregistry.h
===================================================================
--- include/dom/bootstrap/implregistry.h (revision 8909)
+++ include/dom/bootstrap/implregistry.h (working copy)
@@ -15,16 +15,18 @@
struct dom_implementation_list;
struct dom_string;
+/* Initialise the dom_implementation */
+dom_exception dom_implregistry_dom_implementation_initialise(
+ dom_alloc allocator, void *ptr);
+
/* Retrieve a DOM implementation from the registry */
dom_exception dom_implregistry_get_dom_implementation(
struct dom_string *features,
- struct dom_implementation **impl,
- dom_alloc alloc, void *pw);
+ struct dom_implementation **impl);
/* Get a list of DOM implementations that support the requested features */
dom_exception dom_implregistry_get_dom_implementation_list(
struct dom_string *features,
- struct dom_implementation_list **list,
- dom_alloc alloc, void *pw);
+ struct dom_implementation_list **list);
#endif
Index: include/dom/bootstrap/implpriv.h
===================================================================
--- include/dom/bootstrap/implpriv.h (revision 8909)
+++ include/dom/bootstrap/implpriv.h (working copy)
@@ -33,6 +33,7 @@
struct dom_document;
struct dom_document_type;
+struct lwc_context_s;
/**
* DOM Implementation
@@ -61,8 +62,6 @@
* \param public_id The external subset public identifier
* \param system_id The external subset system identifier
* \param doctype Pointer to location to receive result
- * \param alloc Memory (de)allocation function
- * \param pw Pointer to client-specific private data
* \return DOM_NO_ERR on success,
* DOM_INVALID_CHARACTER_ERR if ::qname is invalid,
* DOM_NAMESPACE_ERR if ::qname is malformed,
@@ -83,8 +82,8 @@
struct dom_string *qname,
struct dom_string *public_id,
struct dom_string *system_id,
- struct dom_document_type **doctype,
- dom_alloc alloc, void *pw);
+ dom_alloc alloc, void *pw, struct lwc_context_s *ctx,
+ struct dom_document_type **doctype);
/**
* Create a document node
@@ -94,8 +93,6 @@
* \param qname The qualified name of the document element
* \param doctype The type of document to create
* \param doc Pointer to location to receive result
- * \param alloc Memory (de)allocation function
- * \param pw Pointer to client-specific private data
* \return DOM_NO_ERR on success,
* DOM_INVALID_CHARACTER_ERR if ::qname is invalid,
* DOM_NAMESPACE_ERR if ::qname is malformed, or if
@@ -128,8 +125,8 @@
struct dom_string *namespace,
struct dom_string *qname,
struct dom_document_type *doctype,
- struct dom_document **doc,
- dom_alloc alloc, void *pw);
+ dom_alloc alloc, void *pw, struct lwc_context_s *ctx,
+ struct dom_document **doc);
/**
* Retrieve a specialized object which implements the specified
@@ -139,8 +136,6 @@
* \param feature The requested feature
* \param version The version number of the feature
* \param object Pointer to location to receive object
- * \param alloc Memory (de)allocation function
- * \param pw Pointer to client-specific private data
* \return DOM_NO_ERR.
*
* Any memory allocated by this call should be allocated using
@@ -149,8 +144,7 @@
dom_exception (*get_feature)(struct dom_implementation *impl,
struct dom_string *feature,
struct dom_string *version,
- void **object,
- dom_alloc alloc, void *pw);
+ void **object);
/**
* Destroy a DOM implementation instance
@@ -179,10 +173,14 @@
struct dom_implementation_list {
struct dom_implementation_list_item *head; /**< Head of list */
- dom_alloc alloc; /**< Memory (de)allocation function */
- void *pw; /**< Pointer to client data */
-
uint32_t refcnt; /**< Reference count */
+
+ /**
+ * Destroy a DOM implementation instance
+ *
+ * \param impl The instance to destroy
+ */
+ void (*destroy)(struct dom_implementation_list *impllist);
};
@@ -213,8 +211,7 @@
*/
dom_exception (*get_dom_implementation)(
struct dom_string *features,
- struct dom_implementation **impl,
- dom_alloc alloc, void *pw);
+ struct dom_implementation **impl);
/**
* Get a list of DOM implementations that support the requested
@@ -239,17 +236,16 @@
*/
dom_exception (*get_dom_implementation_list)(
struct dom_string *features,
- struct dom_implementation_list **list,
- dom_alloc alloc, void *pw);
+ struct dom_implementation_list **list);
};
/* Register a source with the DOM library */
-dom_exception dom_register_source(struct dom_implementation_source *source,
- dom_alloc alloc, void *pw);
+dom_exception dom_register_source(struct dom_implementation_source *source);
/* Create a DOM document */
dom_exception dom_document_create(struct dom_implementation *impl,
- dom_alloc alloc, void *pw, struct dom_document **doc);
+ dom_alloc alloc, void *pw, struct lwc_context_s *ctx,
+ struct dom_document **doc);
/* Set a document's buffer */
void dom_document_set_buffer(struct dom_document *doc, uint8_t *buffer,
@@ -259,7 +255,7 @@
dom_exception dom_document_type_create(struct dom_string *qname,
struct dom_string *public_id,
struct dom_string *system_id,
- dom_alloc alloc, void *pw,
+ dom_alloc alloc, void *pw, struct lwc_context_s *ctx,
struct dom_document_type **doctype);
#endif
Index: src/utils/namespace.h
===================================================================
--- src/utils/namespace.h (revision 8909)
+++ src/utils/namespace.h (working copy)
@@ -14,6 +14,7 @@
struct dom_document;
struct dom_string;
+
/* Initialise the namespace component */
dom_exception _dom_namespace_initialise(dom_alloc alloc, void *pw);
@@ -28,5 +29,11 @@
dom_exception _dom_namespace_split_qname(struct dom_string *qname,
struct dom_string **prefix, struct dom_string **localname);
+/* Get the XML prefix dom_string */
+dom_string *_dom_namespace_get_xml_prefix(void);
+
+/* Get the XMLNS prefix dom_string */
+dom_string *_dom_namespace_get_xmlns_prefix(void);
+
#endif
Index: src/utils/Makefile
===================================================================
--- src/utils/Makefile (revision 8909)
+++ src/utils/Makefile (working copy)
@@ -1,4 +1,5 @@
# Sources
-DIR_SOURCES := namespace.c
+DIR_SOURCES := namespace.c hashtable.c resource_mgr.c character_valid.c \
+ validate.c
include build/makefiles/Makefile.subdir
Index: src/utils/namespace.c
===================================================================
--- src/utils/namespace.c (revision 8909)
+++ src/utils/namespace.c (working copy)
@@ -1,8 +1,9 @@
/*
* This file is part of libdom.
* Licensed under the MIT License,
- * http://www.opensource.org/licenses/mit-license.php
+ * http://www.opensource.org/licenses/mit-license.php
* Copyright 2007 John-Mark Bell <jmb(a)netsurf-browser.org>
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
*/
#include <string.h>
@@ -10,6 +11,7 @@
#include <dom/dom.h>
#include "utils/namespace.h"
+#include "utils/validate.h"
#include "utils/utils.h"
@@ -37,7 +39,7 @@
* Initialise the namespace component
*
* \param alloc Pointer to memory (de)allocation function
- * \param pw Pointer to client-specific private data
+ * \param pw Pointer to client-specific private data
* \return DOM_NO_ERR on success.
*/
dom_exception _dom_namespace_initialise(dom_alloc alloc, void *pw)
@@ -110,31 +112,40 @@
/**
* Ensure a QName is valid
*
- * \param qname The qname to validate
+ * \param qname The qname to validate
* \param namespace The namespace URI associated with the QName, or NULL
- * \return DOM_NO_ERR if valid,
- * DOM_INVALID_CHARACTER_ERR if ::qname contains an invalid character,
- * DOM_NAMESPACE_ERR if ::qname is malformed, or it has a
- * prefix and ::namespace is NULL, or
- * ::qname has a prefix "xml" and
- * ::namespace is not
- * "http://www.w3.org/XML/1998/namespace ",
- * or ::qname has a prefix "xmlns" and
- * ::namespace is not
- * "http://www.w3.org/2000/xmlns ", or
- * ::namespace is
- * "http://www.w3.org/2000/xmlns " and
- * ::qname is not (or is not prefixed by)
- * "xmlns".
+ * \return DOM_NO_ERR if valid,
+ * DOM_INVALID_CHARACTER_ERR if ::qname contains an invalid character,
+ * DOM_NAMESPACE_ERR if ::qname is malformed, or it has a
+ * prefix and ::namespace is NULL, or
+ * ::qname has a prefix "xml" and
+ * ::namespace is not
+ * "http://www.w3.org/XML/1998/namespace ",
+ * or ::qname has a prefix "xmlns" and
+ * ::namespace is not
+ * "http://www.w3.org/2000/xmlns ", or
+ * ::namespace is
+ * "http://www.w3.org/2000/xmlns " and
+ * ::qname is not (or is not prefixed by)
+ * "xmlns".
*/
dom_exception _dom_namespace_validate_qname(struct dom_string *qname,
struct dom_string *namespace)
{
- uint32_t colon;
+ uint32_t colon, len;
- /** \todo search qname for invalid characters */
- /** \todo ensure qname is not malformed */
+ if (qname == NULL){
+ if (namespace != NULL)
+ return DOM_NAMESPACE_ERR;
+ if (namespace == NULL)
+ return DOM_NO_ERR;
+ }
+ if (_dom_validate_name(qname) == false)
+ return DOM_NAMESPACE_ERR;
+
+ len = dom_string_length(qname);
+
/* Find colon */
colon = dom_string_index(qname, ':');
@@ -147,9 +158,14 @@
dom_string_cmp(qname, xmlns) != 0) {
return DOM_NAMESPACE_ERR;
}
+ } else if (colon == 0) {
+ /* Some name like ":name" */
+ if (namespace != NULL)
+ return DOM_NAMESPACE_ERR;
} else {
/* Prefix */
struct dom_string *prefix;
+ struct dom_string *lname;
dom_exception err;
/* Ensure there is a namespace URI */
@@ -157,11 +173,21 @@
return DOM_NAMESPACE_ERR;
}
- err = dom_string_substr(qname, 0, colon - 1, &prefix);
+ err = dom_string_substr(qname, 0, colon, &prefix);
if (err != DOM_NO_ERR) {
return err;
}
+ err = dom_string_substr(qname, colon + 1, len, &lname);
+ if (err != DOM_NO_ERR) {
+ return err;
+ }
+
+ if (_dom_validate_ncname(prefix) == false ||
+ _dom_validate_ncname(lname) == false) {
+ return DOM_NAMESPACE_ERR;
+ }
+
/* Test for invalid XML namespace */
if (dom_string_cmp(prefix, xml) == 0 &&
dom_string_cmp(namespace,
@@ -195,8 +221,8 @@
/**
* Split a QName into a namespace prefix and localname string
*
- * \param qname The qname to split
- * \param prefix Pointer to location to receive prefix
+ * \param qname The qname to split
+ * \param prefix Pointer to location to receive prefix
* \param localname Pointer to location to receive localname
* \return DOM_NO_ERR on success.
*
@@ -223,7 +249,7 @@
}
} else {
/* Found one => prefix */
- err = dom_string_substr(qname, 0, colon - 1, prefix);
+ err = dom_string_substr(qname, 0, colon, prefix);
if (err != DOM_NO_ERR) {
return err;
}
@@ -240,3 +266,26 @@
return DOM_NO_ERR;
}
+/* Get the XML prefix dom_string
+ *
+ * Note: The client of this function may or may not call the dom_string_ref
+ * on the returned dom_string, because this string will only be destroyed when
+ * the dom_finalise is called. But if the client call dom_string_ref, it must
+ * call dom_string_unref to maintain a correct ref count of the dom_string.
+ */
+dom_string *_dom_namespace_get_xml_prefix(void)
+{
+ return xml;
+}
+
+/* Get the XMLNS prefix dom_string.
+ *
+ * Note: The client of this function may or may not call the dom_string_ref
+ * on the returned dom_string, because this string will only be destroyed when
+ * the dom_finalise is called. But if the client call dom_string_ref, it must
+ * call dom_string_unref to maintain a correct ref count of the dom_string.
+ */
+dom_string *_dom_namespace_get_xmlns_prefix(void)
+{
+ return xmlns;
+}
Index: src/core/comment.c
===================================================================
--- src/core/comment.c (revision 8909)
+++ src/core/comment.c (working copy)
@@ -3,19 +3,26 @@
* Licensed under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
* Copyright 2007 John-Mark Bell <jmb(a)netsurf-browser.org>
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
*/
#include "core/characterdata.h"
#include "core/comment.h"
#include "core/document.h"
+#include "utils/utils.h"
+
/**
- * A DOM comment node
+ * A DOM Comment node
*/
struct dom_comment {
struct dom_characterdata base; /**< Base node */
};
+static struct dom_node_protect_vtable comment_protect_vtable = {
+ DOM_COMMENT_PROTECT_VTABLE
+};
+
/**
* Create a comment node
*
@@ -31,7 +38,7 @@
* The returned node will already be referenced.
*/
dom_exception dom_comment_create(struct dom_document *doc,
- struct dom_string *name, struct dom_string *value,
+ struct lwc_string_s *name, struct dom_string *value,
struct dom_comment **result)
{
struct dom_comment *c;
@@ -42,6 +49,10 @@
if (c == NULL)
return DOM_NO_MEM_ERR;
+ /* Set the virtual table */
+ ((dom_node_internal *) c)->base.vtable = &characterdata_vtable;
+ ((dom_node_internal *) c)->vtable = &comment_protect_vtable;
+
/* And initialise the node */
err = dom_characterdata_initialise(&c->base, doc, DOM_COMMENT_NODE,
name, value);
@@ -72,3 +83,36 @@
/* Free node */
dom_document_alloc(doc, comment, 0);
}
+
+
+/* The protected virtual functions */
+void _dom_comment_destroy(struct dom_node_internal *node)
+{
+ struct dom_document *doc;
+ doc = dom_node_get_owner(node);
+
+ dom_comment_destroy(doc, (struct dom_comment *) node);
+}
+
+dom_exception _dom_comment_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret)
+{
+ UNUSED(n);
+ dom_comment *c;
+
+ c = dom_document_alloc(doc, NULL, sizeof(struct dom_comment));
+ if (c == NULL)
+ return DOM_NO_MEM_ERR;
+
+ *ret = (dom_node_internal *) c;
+ dom_node_set_owner(*ret, doc);
+
+ return DOM_NO_ERR;
+}
+
+dom_exception _dom_comment_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old)
+{
+ return _dom_characterdata_copy(new, old);
+}
+
Index: src/core/string.c
===================================================================
--- src/core/string.c (revision 8909)
+++ src/core/string.c (working copy)
@@ -3,16 +3,17 @@
* Licensed under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
* Copyright 2007 John-Mark Bell <jmb(a)netsurf-browser.org>
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
*/
+#include <assert.h>
#include <ctype.h>
#include <inttypes.h>
#include <string.h>
#include <parserutils/charset/utf8.h>
-#include <dom/core/string.h>
-
+#include "core/string.h"
#include "core/document.h"
#include "utils/utils.h"
@@ -26,6 +27,10 @@
size_t len; /**< Byte length of string */
+ lwc_string *intern; /**< The lwc_string of this string */
+
+ lwc_context *context; /**< The lwc_context for the lwc_string */
+
dom_alloc alloc; /**< Memory (de)allocation function */
void *pw; /**< Client-specific data */
@@ -40,6 +45,8 @@
.refcnt = 1
};
+
+
/**
* Claim a reference on a DOM string
*
@@ -60,8 +67,15 @@
*/
void dom_string_unref(struct dom_string *str)
{
+ if (str == NULL)
+ return;
+
if (--str->refcnt == 0) {
- if (str->alloc != NULL) {
+ if (str->intern != NULL) {
+ lwc_context_unref(str->context);
+ lwc_context_string_unref(str->context, str->intern);
+ str->alloc(str, 0, str->pw);
+ } else if (str->alloc != NULL) {
str->alloc(str->ptr, 0, str->pw);
str->alloc(str, 0, str->pw);
}
@@ -89,7 +103,7 @@
{
struct dom_string *ret;
- if (ptr == NULL && len == 0) {
+ if (ptr == NULL || len == 0) {
dom_string_ref(&empty_string);
*str = &empty_string;
@@ -114,6 +128,9 @@
ret->alloc = alloc;
ret->pw = pw;
+ ret->intern = NULL;
+ ret->context = NULL;
+
ret->refcnt = 1;
*str = ret;
@@ -121,7 +138,146 @@
return DOM_NO_ERR;
}
+/* Clone a dom_string if necessary. This method is used to create a new string
+ * with a new allocator, but if the allocator is the same with the param str,
+ * just ref the string.
+ *
+ * \param alloc The new allocator for this string
+ * \param pw The new pw for this string
+ * \param str The source dom_string
+ * \param ret The cloned dom_string
+ *
+ * @note: When both the alloc and pw are the same as the str's, we need no
+ * real clone, just ref the source string is ok.
+ */
+dom_exception dom_string_clone(dom_alloc alloc, void *pw, struct dom_string *str,
+ struct dom_string **ret)
+{
+ if (alloc == str->alloc && pw == str->pw) {
+ *ret = str;
+ dom_string_ref(str);
+ return DOM_NO_ERR;
+ }
+
+ if (str->intern != NULL) {
+ return dom_string_create_from_lwcstring(alloc, pw, str->context,
+ str->intern, ret);
+ } else {
+ return dom_string_create(alloc, pw, str->ptr, str->len, ret);
+ }
+}
+
/**
+ * Create a dom_string from a lwc_string
+ *
+ * \param ctx The lwc_context
+ * \param str The lwc_string
+ * \param ret The new dom_string
+ */
+dom_exception dom_string_create_from_lwcstring(dom_alloc alloc, void *pw,
+ lwc_context *ctx, lwc_string *str, struct dom_string **ret)
+{
+ dom_string *r;
+
+ if (str == NULL) {
+ *ret = NULL;
+ return DOM_NO_ERR;
+ }
+
+ r = alloc(NULL, sizeof(struct dom_string), pw);
+ if (r == NULL)
+ return DOM_NO_MEM_ERR;
+
+ if (str == NULL) {
+ *ret = &empty_string;
+ dom_string_ref(*ret);
+ return DOM_NO_ERR;
+ }
+
+ r->context = ctx;
+ r->intern = str;
+ r->ptr = (uint8_t *)lwc_string_data(str);
+ r->len = lwc_string_length(str);
+
+ r->alloc = alloc;
+ r->pw = pw;
+
+ r->refcnt = 1;
+
+ /* Ref the lwc_string */
+ lwc_context_ref(ctx);
+ lwc_context_string_ref(ctx, str);
+
+ *ret = r;
+ return DOM_NO_ERR;
+
+}
+
+/* Make the dom_string be interned in the lwc_context
+ *
+ * \param str The dom_string to be interned
+ * \param ctx The lwc_context to intern this dom_string
+ * \param lwcstr The result lwc_string
+ */
+dom_exception dom_string_intern(struct dom_string *str,
+ struct lwc_context_s *ctx, struct lwc_string_s **lwcstr)
+{
+ lwc_string *ret;
+ lwc_error lerr;
+
+ /* If this string is interned with the same context, do nothing */
+ if (str->context != NULL && str->context == ctx) {
+ *lwcstr = str->intern;
+ return DOM_NO_ERR;
+ }
+
+ lerr = lwc_context_intern(ctx, (const char *)str->ptr, str->len, &ret);
+ if (lerr != lwc_error_ok) {
+ return dom_exception_from_lwc_error(lerr);
+ }
+
+ if (str->context != NULL) {
+ lwc_context_unref(str->context);
+ lwc_context_string_unref(str->context, str->intern);
+ str->ptr = NULL;
+ }
+
+ str->context = ctx;
+ str->intern = ret;
+ lwc_context_ref(ctx);
+ lwc_context_string_ref(ctx, ret);
+
+ if (str->ptr != NULL) {
+ str->alloc(str->ptr, 0, str->pw);
+ }
+
+ str->ptr = (uint8_t *) lwc_string_data(ret);
+
+ *lwcstr = ret;
+ return DOM_NO_ERR;
+}
+
+/* Get the internal lwc_string
+ *
+ * \param str The dom_string object
+ * \param ctx The lwc_context which intern this dom_string
+ * \param lwcstr The lwc_string of this dom-string
+ */
+dom_exception dom_string_get_intern(struct dom_string *str,
+ struct lwc_context_s **ctx, struct lwc_string_s **lwcstr)
+{
+ *ctx = str->context;
+ *lwcstr = str->intern;
+
+ if (*ctx != NULL)
+ lwc_context_ref(*ctx);
+ if (*lwcstr != NULL)
+ lwc_context_string_ref(*ctx, *lwcstr);
+
+ return DOM_NO_ERR;
+}
+
+/**
* Case sensitively compare two DOM strings
*
* \param s1 The first string to compare
@@ -132,12 +288,26 @@
*/
int dom_string_cmp(struct dom_string *s1, struct dom_string *s2)
{
+ bool ret;
+
if (s1 == NULL)
s1 = &empty_string;
if (s2 == NULL)
s2 = &empty_string;
+ if (s1->context == s2->context && s1->context != NULL) {
+ assert(s1->intern != NULL);
+ assert(s2->intern != NULL);
+ lwc_context_string_isequal(s1->context, s1->intern,
+ s2->intern, &ret);
+ if (ret == true) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+
if (s1->len != s2->len)
return 1;
@@ -164,6 +334,19 @@
if (s2 == NULL)
s2 = &empty_string;
+ bool ret;
+ if (s1->context == s2->context && s1->context != NULL) {
+ assert(s1->intern != NULL);
+ assert(s2->intern != NULL);
+ lwc_context_string_caseless_isequal(s1->context, s1->intern,
+ s2->intern, &ret);
+ if (ret == true) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+
d1 = s1->ptr;
d2 = s2->ptr;
l1 = s1->len;
@@ -304,6 +487,55 @@
return clen;
}
+/**
+ * Get the UCS4 character at position index
+ *
+ * \param index The position of the charater
+ * \param ch The UCS4 character
+ */
+dom_exception dom_string_at(struct dom_string *str, uint32_t index,
+ uint32_t *ch)
+{
+ const uint8_t *s;
+ size_t clen, slen;
+ uint32_t c, i;
+ parserutils_error err;
+
+ if (str == NULL)
+ str = &empty_string;
+
+ s = str->ptr;
+ slen = str->len;
+
+ i = 0;
+
+ while (slen > 0) {
+ err = parserutils_charset_utf8_char_byte_length(s, &clen);
+ if (err != PARSERUTILS_OK) {
+ return (uint32_t) -1;
+ }
+
+ i++;
+ if (i == index + 1)
+ break;
+
+ s += clen;
+ slen -= clen;
+ }
+
+ if (i == index + 1) {
+ err = parserutils_charset_utf8_to_ucs4(s, slen, &c, &clen);
+ if (err != PARSERUTILS_OK) {
+ return (uint32_t) -1;
+ }
+
+ *ch = c;
+ return DOM_NO_ERR;
+ } else {
+ return DOM_DOMSTRING_SIZE_ERR;
+ }
+}
+
/**
* Concatenate two dom strings
*
@@ -322,16 +554,33 @@
struct dom_string **result)
{
struct dom_string *concat;
+ dom_alloc alloc;
+ void *pw;
- concat = s1->alloc(NULL, sizeof(struct dom_string), s1->pw);
+ assert(s1 != NULL);
+ assert(s2 != NULL);
+ if (s1->alloc != NULL) {
+ alloc = s1->alloc;
+ pw = s1->pw;
+ } else if (s2->alloc != NULL) {
+ alloc = s2->alloc;
+ pw = s2->pw;
+ } else {
+ /* s1 == s2 == empty_string */
+ *result = &empty_string;
+ return DOM_NO_ERR;
+ }
+
+ concat = alloc(NULL, sizeof(struct dom_string), pw);
+
if (concat == NULL) {
return DOM_NO_MEM_ERR;
}
- concat->ptr = s1->alloc(NULL, s1->len + s2->len, s1->pw);
+ concat->ptr = alloc(NULL, s1->len + s2->len, pw);
if (concat->ptr == NULL) {
- s1->alloc(concat, 0, s1->pw);
+ alloc(concat, 0, pw);
return DOM_NO_MEM_ERR;
}
@@ -342,8 +591,10 @@
concat->len = s1->len + s2->len;
- concat->alloc = s1->alloc;
- concat->pw = s1->pw;
+ concat->alloc = alloc;
+ concat->pw = pw;
+ concat->context = NULL;
+ concat->intern = NULL;
concat->refcnt = 1;
@@ -382,7 +633,7 @@
/* Calculate the byte index of the start */
while (i1 > 0) {
- err = parserutils_charset_utf8_next(s, slen - b1, b1, &b1);
+ err = parserutils_charset_utf8_next(s, slen, b1, &b1);
if (err != PARSERUTILS_OK) {
return DOM_NO_MEM_ERR;
}
@@ -395,7 +646,7 @@
/* Calculate the byte index of the end */
while (i2 > 0) {
- err = parserutils_charset_utf8_next(s, slen - b2, b2, &b2);
+ err = parserutils_charset_utf8_next(s, slen, b2, &b2);
if (err != PARSERUTILS_OK) {
return DOM_NO_MEM_ERR;
}
@@ -451,7 +702,7 @@
ins = tlen;
} else {
while (offset > 0) {
- err = parserutils_charset_utf8_next(t, tlen - ins,
+ err = parserutils_charset_utf8_next(t, tlen,
ins, &ins);
if (err != PARSERUTILS_OK) {
@@ -492,6 +743,8 @@
res->alloc = target->alloc;
res->pw = target->pw;
+ res->intern = NULL;
+ res->context = NULL;
res->refcnt = 1;
@@ -526,6 +779,9 @@
uint32_t b1, b2;
parserutils_error err;
+ if (source == NULL)
+ source = &empty_string;
+
t = target->ptr;
tlen = target->len;
s = source->ptr;
@@ -538,7 +794,7 @@
/* Calculate the byte index of the start */
while (i1 > 0) {
- err = parserutils_charset_utf8_next(s, slen - b1, b1, &b1);
+ err = parserutils_charset_utf8_next(s, slen, b1, &b1);
if (err != PARSERUTILS_OK) {
return DOM_NO_MEM_ERR;
@@ -552,7 +808,7 @@
/* Calculate the byte index of the end */
while (i2 > 0) {
- err = parserutils_charset_utf8_next(s, slen - b2, b2, &b2);
+ err = parserutils_charset_utf8_next(s, slen, b2, &b2);
if (err != PARSERUTILS_OK) {
return DOM_NO_MEM_ERR;
@@ -594,6 +850,8 @@
res->alloc = target->alloc;
res->pw = target->pw;
+ res->intern = NULL;
+ res->context = NULL;
res->refcnt = 1;
@@ -618,8 +876,12 @@
dom_exception dom_string_dup(struct dom_string *str,
struct dom_string **result)
{
- return dom_string_create(str->alloc, str->pw, str->ptr, str->len,
- result);
+ if (str->intern != NULL) {
+ return dom_string_create_from_lwcstring(str->alloc, str->pw, str->context,
+ str->intern, result);
+ } else {
+ return dom_string_create(str->alloc, str->pw, str->ptr, str->len, result);
+ }
}
/**
@@ -645,3 +907,45 @@
return hash;
}
+/* Convert a lwc_error to a dom_exception
+ *
+ * \param err The input lwc_error
+ * \return The dom_exception
+ */
+dom_exception dom_exception_from_lwc_error(lwc_error err)
+{
+ switch (err) {
+ case lwc_error_ok:
+ return DOM_NO_ERR;
+ case lwc_error_oom:
+ return DOM_NO_MEM_ERR;
+ case lwc_error_range:
+ return DOM_INDEX_SIZE_ERR;
+ }
+ assert ("Unknow lwc_error, can't convert to dom_exception");
+ /* Suppress compile errors */
+ return DOM_NO_ERR;
+}
+
+/* Compare the raw data of two lwc_strings for equality when the two string
+ * belong to different lwc_context
+ *
+ * \param s1 The first lwc_string
+ * \param s2 The second lwc_string
+ * \return 0 for equal, non-zeor otherwise
+ */
+int dom_lwc_string_compare_raw(struct lwc_string_s *s1, struct lwc_string_s *s2)
+{
+ const char *rs1, *rs2;
+ size_t len;
+
+ if (lwc_string_length(s1) != lwc_string_length(s2))
+ return -1;
+
+ len = lwc_string_length(s1);
+ rs1 = lwc_string_data(s1);
+ rs2 = lwc_string_data(s2);
+
+ return memcmp(rs1, rs2, len);
+}
+
Index: src/core/comment.h
===================================================================
--- src/core/comment.h (revision 8909)
+++ src/core/comment.h (working copy)
@@ -9,16 +9,33 @@
#define dom_internal_core_comment_h_
#include <dom/core/exceptions.h>
+#include <dom/core/comment.h>
struct dom_comment;
struct dom_document;
struct dom_string;
+struct lwc_string_s;
dom_exception dom_comment_create(struct dom_document *doc,
- struct dom_string *name, struct dom_string *value,
+ struct lwc_string_s *name, struct dom_string *value,
struct dom_comment **result);
+#define dom_comment_initialise dom_characterdata_initialise
+#define dom_comment_finalise dom_characterdata_finalise
+
void dom_comment_destroy(struct dom_document *doc,
struct dom_comment *comment);
+/* Following comes the protected vtable */
+void _dom_comment_destroy(struct dom_node_internal *node);
+dom_exception _dom_comment_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret);
+dom_exception _dom_comment_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old);
+
+#define DOM_COMMENT_PROTECT_VTABLE \
+ _dom_comment_destroy, \
+ _dom_comment_alloc, \
+ _dom_comment_copy
+
#endif
Index: src/core/nodelist.c
===================================================================
--- src/core/nodelist.c (revision 8909)
+++ src/core/nodelist.c (working copy)
@@ -3,12 +3,17 @@
* Licensed under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
* Copyright 2007 John-Mark Bell <jmb(a)netsurf-browser.org>
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
*/
+#include <assert.h>
#include <dom/core/node.h>
+#include <dom/core/document.h>
#include <dom/core/nodelist.h>
#include <dom/core/string.h>
+#include <libwapcaplet/libwapcaplet.h>
+
#include "core/document.h"
#include "core/node.h"
#include "core/nodelist.h"
@@ -23,16 +28,18 @@
struct dom_node_internal *root; /**< Root of applicable subtree */
- enum { DOM_NODELIST_CHILDREN,
- DOM_NODELIST_BY_NAME,
- DOM_NODELIST_BY_NAMESPACE
- } type; /**< List type */
+ nodelist_type type;
union {
- struct dom_string *name; /**< Tag name to match */
struct {
- struct dom_string *namespace; /**< Namespace */
- struct dom_string *localname; /**< Localname */
+ struct lwc_string_s *name; /**< Tag name to match */
+ bool any_name; /**< The name is '*' */
+ } n;
+ struct {
+ bool any_namespace; /**< The namespace is '*' */
+ bool any_localname; /**< The localname is '*' */
+ struct lwc_string_s *namespace; /**< Namespace */
+ struct lwc_string_s *localname; /**< Localname */
} ns; /**< Data for namespace matching */
} data;
@@ -43,6 +50,7 @@
* Create a nodelist
*
* \param doc Owning document
+ * \param type The type of the NodeList
* \param root Root node of subtree that list applies to
* \param tagname Name of nodes in list (or NULL)
* \param namespace Namespace part of nodes in list (or NULL)
@@ -52,26 +60,20 @@
*
* ::root must be a node owned by ::doc
*
- * If ::tagname is non-NULL, ::namespace and ::localname must be NULL and
- * the created list will match nodes by name
- *
- * If ::namespace is non-NULL, ::localname must be non-NULL and
- * ::tagname must be NULL and the created list will match nodes by namespace
- * and localname
- *
- * If ::tagname, ::namespace and ::localname are NULL, the created list
- * will match the children of ::root.
- *
* The returned list will already be referenced, so the client need not
* do so explicitly. The client must unref the list once finished with it.
*/
-dom_exception dom_nodelist_create(struct dom_document *doc,
- struct dom_node_internal *root, struct dom_string *tagname,
- struct dom_string *namespace, struct dom_string *localname,
+dom_exception dom_nodelist_create(struct dom_document *doc, nodelist_type type,
+ struct dom_node_internal *root, struct lwc_string_s *tagname,
+ struct lwc_string_s *namespace, struct lwc_string_s *localname,
struct dom_nodelist **list)
{
struct dom_nodelist *l;
+ lwc_context *ctx;
+ ctx = dom_document_get_intern_context(doc);
+ assert(ctx != NULL);
+
l = dom_document_alloc(doc, NULL, sizeof(struct dom_nodelist));
if (l == NULL)
return DOM_NO_MEM_ERR;
@@ -82,20 +84,45 @@
dom_node_ref(root);
l->root = root;
- if (tagname != NULL && namespace == NULL && localname == NULL) {
- dom_string_ref(tagname);
- l->type = DOM_NODELIST_BY_NAME;
- l->data.name = tagname;
- } else if (namespace != NULL && localname != NULL &&
- tagname == NULL) {
- dom_string_ref(namespace);
- dom_string_ref(localname);
- l->type = DOM_NODELIST_BY_NAMESPACE;
+ l->type = type;
+
+ if (type == DOM_NODELIST_BY_NAME) {
+ assert(tagname != NULL);
+ l->data.n.any_name = false;
+ if (lwc_string_length(tagname) == 1) {
+ const char *ch = lwc_string_data(tagname);
+ if (*ch == '*') {
+ l->data.n.any_name = true;
+ }
+ }
+
+ lwc_context_string_ref(ctx, tagname);
+ l->data.n.name = tagname;
+ } else if (type == DOM_NODELIST_BY_NAMESPACE) {
+ l->data.ns.any_localname = false;
+ l->data.ns.any_namespace = false;
+ if (localname != NULL) {
+ if (lwc_string_length(localname) == 1) {
+ const char *ch = lwc_string_data(localname);
+ if (*ch == '*') {
+ l->data.ns.any_localname = true;
+ }
+ }
+ lwc_context_string_ref(ctx, localname);
+ }
+ if (namespace != NULL) {
+ if (lwc_string_length(namespace) == 1) {
+ const char *ch = lwc_string_data(namespace);
+ if (*ch == '*') {
+ l->data.ns.any_namespace = true;
+ }
+ }
+ lwc_context_string_ref(ctx, namespace);
+ }
+
l->data.ns.namespace = namespace;
l->data.ns.localname = localname;
- } else {
- l->type = DOM_NODELIST_CHILDREN;
- }
+ }
l->refcnt = 1;
@@ -111,6 +138,7 @@
*/
void dom_nodelist_ref(struct dom_nodelist *list)
{
+ assert(list != NULL);
list->refcnt++;
}
@@ -124,20 +152,29 @@
*/
void dom_nodelist_unref(struct dom_nodelist *list)
{
+ if (list == NULL)
+ return;
+
if (--list->refcnt == 0) {
struct dom_node_internal *owner =
(struct dom_node_internal *) list->owner;
+ lwc_context *ctx;
+ ctx = dom_document_get_intern_context((dom_document *) owner);
+ assert(ctx != NULL);
switch (list->type) {
case DOM_NODELIST_CHILDREN:
/* Nothing to do */
break;
case DOM_NODELIST_BY_NAMESPACE:
- dom_string_unref(list->data.ns.namespace);
- dom_string_unref(list->data.ns.localname);
+ if (list->data.ns.namespace != NULL)
+ lwc_context_string_unref(ctx, list->data.ns.namespace);
+ if (list->data.ns.localname != NULL)
+ lwc_context_string_unref(ctx, list->data.ns.localname);
break;
case DOM_NODELIST_BY_NAME:
- dom_string_unref(list->data.name);
+ assert(list->data.n.name != NULL);
+ lwc_context_string_unref(ctx, list->data.n.name);
break;
}
@@ -175,19 +212,22 @@
if (list->type == DOM_NODELIST_CHILDREN) {
len++;
} else if (list->type == DOM_NODELIST_BY_NAME) {
- if (cur->name != NULL &&
- dom_string_cmp(cur->name,
- list->data.name) == 0) {
- len++;
+ /* Here, we compare two lwc_string pointer directly */
+ if (list->data.n.any_name == true || (
+ cur->name != NULL &&
+ cur->name == list->data.n.name)) {
+ if (cur->type == DOM_ELEMENT_NODE)
+ len++;
}
} else {
- if (cur->namespace != NULL &&
- dom_string_cmp(cur->namespace,
- list->data.ns.namespace) == 0 &&
- cur->name != NULL &&
- dom_string_cmp(cur->name,
- list->data.ns.localname) == 0) {
- len++;
+ if (list->data.ns.any_namespace == true ||
+ cur->namespace == list->data.ns.namespace) {
+ if (list->data.ns.any_localname == true ||
+ (cur->name != NULL &&
+ cur->name == list->data.ns.localname)) {
+ if (cur->type == DOM_ELEMENT_NODE)
+ len++;
+ }
}
}
@@ -250,19 +290,22 @@
if (list->type == DOM_NODELIST_CHILDREN) {
count++;
} else if (list->type == DOM_NODELIST_BY_NAME) {
- if (cur->name != NULL &&
- dom_string_cmp(cur->name,
- list->data.name) == 0) {
- count++;
+ if (list->data.n.any_name == true || (
+ cur->name != NULL &&
+ cur->name == list->data.n.name)) {
+ if (cur->type == DOM_ELEMENT_NODE)
+ count++;
}
} else {
- if (cur->namespace != NULL &&
- dom_string_cmp(cur->namespace,
- list->data.ns.namespace) == 0 &&
- cur->name != NULL &&
- dom_string_cmp(cur->name,
- list->data.ns.localname) == 0) {
- count++;
+ if (list->data.ns.any_namespace == true ||
+ (cur->namespace != NULL &&
+ cur->namespace == list->data.ns.namespace)) {
+ if (list->data.ns.any_localname == true ||
+ (cur->name != NULL &&
+ cur->name == list->data.ns.localname)) {
+ if (cur->type == DOM_ELEMENT_NODE)
+ count++;
+ }
}
}
@@ -311,36 +354,44 @@
* Match a nodelist instance against a set of nodelist creation parameters
*
* \param list List to match
+ * \param type The type of the NodeList
* \param root Root node of subtree that list applies to
* \param tagname Name of nodes in list (or NULL)
* \param namespace Namespace part of nodes in list (or NULL)
* \param localname Local part of nodes in list (or NULL)
* \return true if list matches, false otherwise
*/
-bool dom_nodelist_match(struct dom_nodelist *list,
- struct dom_node_internal *root, struct dom_string *tagname,
- struct dom_string *namespace, struct dom_string *localname)
+bool dom_nodelist_match(struct dom_nodelist *list, nodelist_type type,
+ struct dom_node_internal *root, struct lwc_string_s *tagname,
+ struct lwc_string_s *namespace, struct lwc_string_s *localname)
{
if (list->root != root)
return false;
- if (list->type == DOM_NODELIST_CHILDREN && tagname == NULL &&
- namespace == NULL && localname == NULL) {
+ if (list->type != type)
+ return false;
+
+ if (list->type == DOM_NODELIST_CHILDREN) {
return true;
}
- if (list->type == DOM_NODELIST_BY_NAME && tagname != NULL &&
- namespace == NULL && localname == NULL) {
- return (dom_string_cmp(list->data.name, tagname) == 0);
+ if (list->type == DOM_NODELIST_BY_NAME) {
+ return (list->data.n.name == tagname);
}
- if (list->type == DOM_NODELIST_BY_NAMESPACE && tagname == NULL &&
- namespace != NULL && localname != NULL) {
- return (dom_string_cmp(list->data.ns.namespace,
- namespace) == 0) &&
- (dom_string_cmp(list->data.ns.localname,
- localname) == 0);
+ if (list->type == DOM_NODELIST_BY_NAMESPACE) {
+ return (list->data.ns.namespace == namespace) &&
+ (list->data.ns.localname == localname);
}
return false;
}
+
+/*
+ * Test whether the two NodeList are equal
+ */
+bool _dom_nodelist_equal(struct dom_nodelist *l1, struct dom_nodelist *l2)
+{
+ return dom_nodelist_match(l1, l1->type, l2->root, l2->data.n.name,
+ l2->data.ns.namespace, l2->data.ns.localname);
+}
Index: src/core/text.c
===================================================================
--- src/core/text.c (revision 8909)
+++ src/core/text.c (working copy)
@@ -3,18 +3,22 @@
* Licensed under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
* Copyright 2007 John-Mark Bell <jmb(a)netsurf-browser.org>
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
*/
+#include <assert.h>
#include <dom/core/string.h>
#include <dom/core/text.h>
+#include <libwapcaplet/libwapcaplet.h>
+
#include "core/characterdata.h"
#include "core/document.h"
#include "core/text.h"
#include "utils/utils.h"
/* The virtual table for dom_text */
-static struct dom_text_vtable text_vtable = {
+struct dom_text_vtable text_vtable = {
{
{
DOM_NODE_VTABLE
@@ -24,16 +28,33 @@
DOM_TEXT_VTABLE
};
-/* The destroy virtual function */
-void _dom_text_destroy(struct dom_node_internal *node);
-void _dom_text_destroy(struct dom_node_internal *node)
-{
- struct dom_document *doc;
- dom_node_get_owner_document(node, &doc);
+static struct dom_node_protect_vtable text_protect_vtable = {
+ DOM_TEXT_PROTECT_VTABLE
+};
- dom_text_destroy(doc, (struct dom_text *) node);
-}
+/* Following comes helper functions */
+typedef enum walk_operation {
+ COLLECT,
+ DELETE
+} walk_operation;
+typedef enum walk_order {
+ LEFT,
+ RIGHT
+} walk_order;
+/* Walk the logic-adjacent text in document order */
+static dom_exception walk_logic_adjacent_text_in_order(
+ dom_node_internal *node, walk_operation opt,
+ walk_order order, dom_string **ret, bool *cont);
+/* Walk the logic-adjacent text */
+static dom_exception walk_logic_adjacent_text(dom_text *text,
+ walk_operation opt, dom_string **ret);
+
+/* */
+/*----------------------------------------------------------------------*/
+/* */
+/* Constructor and Destructor */
+
/**
* Create a text node
*
@@ -49,7 +70,7 @@
* The returned node will already be referenced.
*/
dom_exception dom_text_create(struct dom_document *doc,
- struct dom_string *name, struct dom_string *value,
+ struct lwc_string_s *name, struct dom_string *value,
struct dom_text **result)
{
struct dom_text *t;
@@ -67,6 +88,10 @@
return err;
}
+ /* Compose the vtable */
+ ((struct dom_node *) t)->vtable = &text_vtable;
+ ((struct dom_node_internal *) t)->vtable = &text_protect_vtable;
+
*result = t;
return DOM_NO_ERR;
@@ -103,7 +128,7 @@
*/
dom_exception dom_text_initialise(struct dom_text *text,
struct dom_document *doc, dom_node_type type,
- struct dom_string *name, struct dom_string *value)
+ struct lwc_string_s *name, struct dom_string *value)
{
dom_exception err;
@@ -113,10 +138,6 @@
if (err != DOM_NO_ERR)
return err;
- /* Compose the vtable */
- ((struct dom_node *) text)->vtable = &text_vtable;
- text->base.base.destroy = &_dom_text_destroy;
-
/* Perform our type-specific initialisation */
text->element_content_whitespace = false;
@@ -136,6 +157,11 @@
dom_characterdata_finalise(doc, &text->base);
}
+/* */
+/*----------------------------------------------------------------------*/
+/* */
+/* The public virtual functions */
+
/**
* Split a text node at a given character offset
*
@@ -227,10 +253,7 @@
dom_exception _dom_text_get_whole_text(struct dom_text *text,
struct dom_string **result)
{
- UNUSED(text);
- UNUSED(result);
-
- return DOM_NOT_SUPPORTED_ERR;
+ return walk_logic_adjacent_text(text, COLLECT, result);
}
/**
@@ -249,10 +272,230 @@
dom_exception _dom_text_replace_whole_text(struct dom_text *text,
struct dom_string *content, struct dom_text **result)
{
- UNUSED(text);
- UNUSED(content);
- UNUSED(result);
+ dom_exception err;
+ dom_string *ret;
- return DOM_NOT_SUPPORTED_ERR;
+ err = walk_logic_adjacent_text(text, DELETE, &ret);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ err = dom_characterdata_set_data(text, content);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ *result = text;
+ dom_node_ref(text);
+
+ return DOM_NO_ERR;
}
+/* The protected virtual functions */
+void _dom_text_destroy(struct dom_node_internal *node)
+{
+ struct dom_document *doc;
+ doc = dom_node_get_owner(node);
+
+ dom_text_destroy(doc, (struct dom_text *) node);
+}
+
+dom_exception _dom_text_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret)
+{
+ UNUSED(n);
+ dom_text *a;
+
+ a = dom_document_alloc(doc, NULL, sizeof(struct dom_text));
+ if (a == NULL)
+ return DOM_NO_MEM_ERR;
+
+ *ret = (dom_node_internal *) a;
+ dom_node_set_owner(*ret, doc);
+
+ return DOM_NO_ERR;
+}
+
+dom_exception _dom_text_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old)
+{
+ dom_text *ot = (dom_text *) old;
+ dom_text *nt = (dom_text *) new;
+
+ nt->element_content_whitespace = ot->element_content_whitespace;
+
+ return _dom_characterdata_copy(new, old);
+}
+
+/* */
+/*----------------------------------------------------------------------*/
+/* */
+/* Helper functions */
+
+/**
+ * Walk the logic adjacent text in certain order
+ *
+ * \param node The start Text node
+ * \param opt The operation on each Text Node
+ * \param order The order
+ * \param ret The string of the logic adjacent text
+ * \param cont Whether the logic adjacent text is interrupt here
+ */
+dom_exception walk_logic_adjacent_text_in_order(
+ dom_node_internal *node, walk_operation opt,
+ walk_order order, dom_string **ret, bool *cont)
+{
+ dom_exception err;
+ dom_string *data, *tmp;
+ dom_node_internal *parent = dom_node_get_parent(node);
+
+ /* If we reach the leaf of the DOM tree, just return to continue
+ * to next sibling of our parent */
+ if (node == NULL) {
+ *cont = true;
+ return DOM_NO_ERR;
+ }
+
+ while (node != NULL) {
+ /* If we reach the boundary of logical-adjacent text, we stop */
+ if (node->type == DOM_ELEMENT_NODE || node->type == DOM_COMMENT_NODE
+ || node->type == DOM_PROCESSING_INSTRUCTION_NODE) {
+ *cont = false;
+ return DOM_NO_ERR;
+ }
+
+ if (node->type == DOM_TEXT_NODE) {
+ /* According the DOM spec, text node never have child */
+ assert(node->first_child == NULL);
+ assert(node->last_child == NULL);
+ if (opt == COLLECT) {
+ err = dom_characterdata_get_data(node, &data);
+ if (err == DOM_NO_ERR)
+ return err;
+
+ tmp = *ret;
+ if (order == LEFT) {
+ err = dom_string_concat(data, tmp, ret);
+ if (err == DOM_NO_ERR)
+ return err;
+ } else if (order == RIGHT) {
+ err = dom_string_concat(tmp, data, ret);
+ if (err == DOM_NO_ERR)
+ return err;
+ }
+
+ dom_string_unref(tmp);
+ dom_string_unref(data);
+
+ *cont = true;
+ return DOM_NO_ERR;
+ }
+
+ if (opt == DELETE) {
+ dom_node_internal *tn;
+ err = dom_node_remove_child(node->parent, node, (void *) &tn);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ *cont = true;
+ dom_node_unref(tn);
+ return DOM_NO_ERR;
+ }
+ }
+
+ dom_node_internal *p = dom_node_get_parent(node);
+ if (order == LEFT) {
+ if (node->last_child != NULL) {
+ node = node->last_child;
+ } else if (node->previous != NULL) {
+ node = node->previous;
+ } else {
+ while (p != parent && node == p->last_child) {
+ node = p;
+ p = dom_node_get_parent(p);
+ }
+
+ node = node->previous;
+ }
+ } else {
+ if (node->first_child != NULL) {
+ node = node->first_child;
+ } else if (node->next != NULL) {
+ node = node->next;
+ } else {
+ while (p != parent && node == p->first_child) {
+ node = p;
+ p = dom_node_get_parent(p);
+ }
+
+ node = node->next;
+ }
+ }
+ }
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Traverse the logic adjacent text.
+ *
+ * \param text The Text Node from which we start traversal
+ * \param opt The operation code
+ * \param ret The returned string if the opt is COLLECT
+ */
+dom_exception walk_logic_adjacent_text(dom_text *text,
+ walk_operation opt, dom_string **ret)
+{
+ dom_node_internal *node = (dom_node_internal *) text;
+ dom_node_internal *parent = node->parent;
+ dom_node_internal *left = node->previous;
+ dom_node_internal *right = node->next;
+ dom_exception err;
+ bool cont;
+
+ if (parent->type == DOM_ENTITY_NODE) {
+ return DOM_NOT_SUPPORTED_ERR;
+ }
+
+ /* Firstly, we look our left */
+ err = walk_logic_adjacent_text_in_order(left, opt, LEFT, ret, &cont);
+ if (err != DOM_NO_ERR) {
+ dom_string_unref(*ret);
+ *ret = NULL;
+ return err;
+ }
+
+ /* Ourself */
+ if (opt == COLLECT) {
+ dom_string *data = NULL, *tmp = NULL;
+ err = dom_characterdata_get_data(text, &data);
+ if (err == DOM_NO_ERR) {
+ dom_string_unref(*ret);
+ return err;
+ }
+
+ err = dom_string_concat(*ret, data, &tmp);
+ if (err == DOM_NO_ERR) {
+ dom_string_unref(*ret);
+ return err;
+ }
+
+ dom_string_unref(*ret);
+ dom_string_unref(data);
+ *ret = tmp;
+ } else {
+ dom_node_internal *tn;
+ err = dom_node_remove_child(node->parent, node, (void *) &tn);
+ if (err != DOM_NO_ERR)
+ return err;
+ dom_node_unref(tn);
+ }
+
+ /* Now, look right */
+ err = walk_logic_adjacent_text_in_order(right, opt, RIGHT, ret, &cont);
+ if (err != DOM_NO_ERR) {
+ dom_string_unref(*ret);
+ *ret = NULL;
+ return err;
+ }
+
+ return DOM_NO_ERR;
+}
Index: src/core/nodelist.h
===================================================================
--- src/core/nodelist.h (revision 8909)
+++ src/core/nodelist.h (working copy)
@@ -16,16 +16,23 @@
struct dom_node;
struct dom_nodelist;
struct dom_string;
+struct lwc_string_s;
+typedef enum {
+ DOM_NODELIST_CHILDREN,
+ DOM_NODELIST_BY_NAME,
+ DOM_NODELIST_BY_NAMESPACE
+} nodelist_type;
+
/* Create a nodelist */
-dom_exception dom_nodelist_create(struct dom_document *doc,
- struct dom_node_internal *root, struct dom_string *tagname,
- struct dom_string *namespace, struct dom_string *localname,
+dom_exception dom_nodelist_create(struct dom_document *doc, nodelist_type type,
+ struct dom_node_internal *root, struct lwc_string_s *tagname,
+ struct lwc_string_s *namespace, struct lwc_string_s *localname,
struct dom_nodelist **list);
/* Match a nodelist instance against a set of nodelist creation parameters */
-bool dom_nodelist_match(struct dom_nodelist *list,
- struct dom_node_internal *root, struct dom_string *tagname,
- struct dom_string *namespace, struct dom_string *localname);
+bool dom_nodelist_match(struct dom_nodelist *list, nodelist_type type,
+ struct dom_node_internal *root, struct lwc_string_s *tagname,
+ struct lwc_string_s *namespace, struct lwc_string_s *localname);
#endif
Index: src/core/characterdata.c
===================================================================
--- src/core/characterdata.c (revision 8909)
+++ src/core/characterdata.c (working copy)
@@ -3,8 +3,11 @@
* Licensed under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
* Copyright 2007 John-Mark Bell <jmb(a)netsurf-browser.org>
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
*/
+#include <assert.h>
+
#include <dom/core/characterdata.h>
#include <dom/core/string.h>
@@ -13,8 +16,9 @@
#include "core/node.h"
#include "utils/utils.h"
-/* The virtual functions for dom_characterdata */
-static struct dom_characterdata_vtable characterdata_vtable = {
+/* The virtual functions for dom_characterdata, we make this vtable
+ * public to each child class */
+struct dom_characterdata_vtable characterdata_vtable = {
{
DOM_NODE_VTABLE
},
@@ -35,6 +39,8 @@
return NULL;
cdata->base.base.vtable = &characterdata_vtable;
+ cdata->base.vtable = NULL;
+
return cdata;
}
@@ -52,7 +58,7 @@
*/
dom_exception dom_characterdata_initialise(struct dom_characterdata *cdata,
struct dom_document *doc, dom_node_type type,
- struct dom_string *name, struct dom_string *value)
+ lwc_string *name, struct dom_string *value)
{
return dom_node_initialise(&cdata->base, doc, type,
name, value, NULL, NULL);
@@ -72,6 +78,11 @@
dom_node_finalise(doc, &cdata->base);
}
+/* */
+/*----------------------------------------------------------------------*/
+/* */
+/* The public virtual functions */
+
/**
* Retrieve data from a character data node
*
@@ -182,7 +193,7 @@
len = 0;
}
- if (offset >= len) {
+ if (offset > len) {
return DOM_INDEX_SIZE_ERR;
}
@@ -210,9 +221,7 @@
return DOM_NO_MODIFICATION_ALLOWED_ERR;
}
- err = dom_string_insert(c->value, data,
- c->value != NULL ? dom_string_length(c->value) : 0,
- &temp);
+ err = dom_string_concat(c->value, data, &temp);
if (err != DOM_NO_ERR) {
return err;
}
@@ -255,7 +264,7 @@
len = 0;
}
- if (offset >= len) {
+ if (offset > len) {
return DOM_INDEX_SIZE_ERR;
}
@@ -302,7 +311,7 @@
len = 0;
}
- if (offset >= len) {
+ if (offset > len) {
return DOM_INDEX_SIZE_ERR;
}
@@ -353,7 +362,7 @@
len = 0;
}
- if (offset >= len) {
+ if (offset > len) {
return DOM_INDEX_SIZE_ERR;
}
@@ -373,3 +382,36 @@
return DOM_NO_ERR;
}
+
+/* */
+/*----------------------------------------------------------------------*/
+/* */
+/* The protected virtual functions of Node, see core/node.h for details
+ *
+ * @note: the three following API never be called directly from the virtual
+ * functions dispatch mechanism, they are here for the code consistent.
+ */
+void _dom_characterdata_destroy(struct dom_node_internal *node)
+{
+ assert("Should never be here" == NULL);
+ UNUSED(node);
+}
+
+dom_exception _dom_characterdata_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret)
+{
+ assert("Should never be here" == NULL);
+ UNUSED(doc);
+ UNUSED(n);
+ UNUSED(ret);
+
+ return DOM_NO_ERR;
+}
+
+/* The sub-class of characterdata should call this API */
+dom_exception _dom_characterdata_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old)
+{
+ return _dom_node_copy(new, old);
+}
+
Index: src/core/doc_fragment.c
===================================================================
--- src/core/doc_fragment.c (revision 8909)
+++ src/core/doc_fragment.c (working copy)
@@ -3,13 +3,17 @@
* Licensed under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
* Copyright 2007 John-Mark Bell <jmb(a)netsurf-browser.org>
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
*/
+#include <libwapcaplet/libwapcaplet.h>
+
#include <dom/core/node.h>
#include "core/document.h"
#include "core/doc_fragment.h"
#include "core/node.h"
+#include "utils/utils.h"
/**
* A DOM document fragment
@@ -18,8 +22,14 @@
struct dom_node_internal base; /**< Base node */
};
-void _dom_document_fragment_destroy(struct dom_node_internal *node);
+static struct dom_node_vtable df_vtable = {
+ DOM_NODE_VTABLE
+};
+static struct dom_node_protect_vtable df_protect_vtable = {
+ DOM_DF_PROTECT_VTABLE
+};
+
/**
* Create a document fragment
*
@@ -35,7 +45,7 @@
* The returned node will already be referenced.
*/
dom_exception dom_document_fragment_create(struct dom_document *doc,
- struct dom_string *name, struct dom_string *value,
+ struct lwc_string_s *name, struct dom_string *value,
struct dom_document_fragment **result)
{
struct dom_document_fragment *f;
@@ -47,17 +57,18 @@
if (f == NULL)
return DOM_NO_MEM_ERR;
+
+ f->base.base.vtable = &df_vtable;
+ f->base.vtable = &df_protect_vtable;
+
/* And initialise the node */
- err = dom_node_initialise(&f->base, doc, DOM_DOCUMENT_FRAGMENT_NODE,
- name, value, NULL, NULL);
+ err = dom_document_fragment_initialise(&f->base, doc,
+ DOM_DOCUMENT_FRAGMENT_NODE, name, value, NULL, NULL);
if (err != DOM_NO_ERR) {
dom_document_alloc(doc, f, 0);
return err;
}
- /* Set the virtual function of destroy */
- f->base.destroy = &_dom_document_fragment_destroy;
-
*result = f;
return DOM_NO_ERR;
@@ -74,42 +85,38 @@
void dom_document_fragment_destroy(struct dom_document *doc,
struct dom_document_fragment *frag)
{
- struct dom_node_internal *c, *d;
-
- /* Destroy children of this node */
- for (c = frag->base.first_child; c != NULL; c = d) {
- d = c->next;
-
- /* Detach child */
- c->parent = NULL;
-
- if (c->refcnt > 0) {
- /* Something is using this child */
-
- /** \todo add to list of nodes pending deletion */
-
- continue;
- }
-
- /* Detach from sibling list */
- c->previous = NULL;
- c->next = NULL;
-
- dom_node_destroy(c);
- }
-
/* Finalise base class */
- dom_node_finalise(doc, &frag->base);
+ dom_document_fragment_finalise(doc, &frag->base);
/* Destroy fragment */
dom_document_alloc(doc, frag, 0);
}
-void _dom_document_fragment_destroy(struct dom_node_internal *node)
+/* Overload protected functions */
+void _dom_df_destroy(struct dom_node_internal *node)
{
- struct dom_document *doc;
- dom_node_get_owner_document(node, &doc);
-
- dom_document_fragment_destroy(doc,
+ dom_document_fragment_destroy(node->owner,
(struct dom_document_fragment *) node);
}
+
+dom_exception _dom_df_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret)
+{
+ UNUSED(n);
+ struct dom_document_fragment *a;
+
+ a = dom_document_alloc(doc, NULL, sizeof(struct dom_document_fragment));
+ if (a == NULL)
+ return DOM_NO_MEM_ERR;
+
+ *ret = (dom_node_internal *) a;
+ dom_node_set_owner(*ret, doc);
+
+ return DOM_NO_ERR;
+}
+
+dom_exception _dom_df_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old)
+{
+ return _dom_node_copy(new, old);
+}
Index: src/core/text.h
===================================================================
--- src/core/text.h (revision 8909)
+++ src/core/text.h (working copy)
@@ -16,6 +16,8 @@
struct dom_document;
struct dom_string;
+struct lwc_context_s;
+struct lwc_string_s;
/**
* A DOM text node
@@ -27,6 +29,20 @@
* content whitespace */
};
+/* Constructor and Destructor */
+dom_exception dom_text_create(struct dom_document *doc,
+ struct lwc_string_s *name, struct dom_string *value,
+ struct dom_text **result);
+
+void dom_text_destroy(struct dom_document *doc, struct dom_text *text);
+
+dom_exception dom_text_initialise(struct dom_text *text,
+ struct dom_document *doc, dom_node_type type,
+ struct lwc_string_s *name, struct dom_string *value);
+
+void dom_text_finalise(struct dom_document *doc, struct dom_text *text);
+
+
/* Vitual functions for dom_text */
dom_exception _dom_text_split_text(struct dom_text *text,
unsigned long offset, struct dom_text **result);
@@ -37,22 +53,26 @@
dom_exception _dom_text_replace_whole_text(struct dom_text *text,
struct dom_string *content, struct dom_text **result);
-dom_exception dom_text_create(struct dom_document *doc,
- struct dom_string *name, struct dom_string *value,
- struct dom_text **result);
-
-void dom_text_destroy(struct dom_document *doc, struct dom_text *text);
-
-dom_exception dom_text_initialise(struct dom_text *text,
- struct dom_document *doc, dom_node_type type,
- struct dom_string *name, struct dom_string *value);
-
-void dom_text_finalise(struct dom_document *doc, struct dom_text *text);
-
#define DOM_TEXT_VTABLE \
_dom_text_split_text, \
_dom_text_get_is_element_content_whitespace, \
_dom_text_get_whole_text, \
_dom_text_replace_whole_text
+
+/* Following comes the protected vtable */
+void _dom_text_destroy(struct dom_node_internal *node);
+dom_exception _dom_text_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret);
+dom_exception _dom_text_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old);
+
+#define DOM_TEXT_PROTECT_VTABLE \
+ _dom_text_destroy, \
+ _dom_text_alloc, \
+ _dom_text_copy
+
+
+extern struct dom_text_vtable text_vtable;
+
#endif
Index: src/core/element.c
===================================================================
--- src/core/element.c (revision 8909)
+++ src/core/element.c (working copy)
@@ -3,31 +3,130 @@
* Licensed under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
* Copyright 2007 John-Mark Bell <jmb(a)netsurf-browser.org>
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
*/
#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <dom/dom.h>
#include <dom/core/attr.h>
#include <dom/core/element.h>
#include <dom/core/node.h>
#include <dom/core/string.h>
+#include <dom/core/document.h>
#include "core/attr.h"
#include "core/document.h"
#include "core/element.h"
#include "core/node.h"
+#include "core/namednodemap.h"
+#include "utils/validate.h"
#include "utils/namespace.h"
#include "utils/utils.h"
+#include "utils/hashtable.h"
+/* The two numbers are just random ones, maybe we should change it after some
+ * more consideration */
+#define CHAINS_ATTRIBUTES 31
+#define CHAINS_NAMESPACE 7
+#define CHAINS_NS_ATTRIBUTES 31
+
+/* TODO: Make the "id" definition temporarily here, in future, it should
+ * be obtained from the language bindings.
+ */
+#define ID_ATTRIBUTE "id"
+
static struct dom_element_vtable element_vtable = {
{
- DOM_NODE_VTABLE
+ DOM_NODE_VTABLE_ELEMENT
},
DOM_ELEMENT_VTABLE
};
+static struct dom_node_protect_vtable element_protect_vtable = {
+ DOM_ELEMENT_PROTECT_VTABLE
+};
+
+static dom_exception _dom_element_get_attr(struct dom_element *element,
+ struct hash_table *hs, struct dom_string *name,
+ struct dom_string **value);
+static dom_exception _dom_element_set_attr(struct dom_element *element,
+ struct hash_table *hs, struct dom_string *name,
+ struct dom_string *value);
+static dom_exception _dom_element_remove_attr(struct dom_element *element,
+ struct hash_table *hs, struct dom_string *name);
+static dom_exception _dom_element_get_attr_node(struct dom_element *element,
+ struct hash_table *hs, struct dom_string *name,
+ struct dom_attr **result);
+static dom_exception _dom_element_set_attr_node(struct dom_element *element,
+ struct hash_table *hs, struct dom_attr *attr,
+ struct dom_attr **result);
+static dom_exception _dom_element_remove_attr_node(struct dom_element *element,
+ struct hash_table *hs, struct dom_attr *attr,
+ struct dom_attr **result);
+static dom_exception _dom_element_has_attr(struct dom_element *element,
+ struct hash_table *hs, struct dom_string *name, bool *result);
+static dom_exception _dom_element_set_id_attr(struct dom_element *element,
+ struct hash_table *hs, struct dom_string *name, bool is_id);
+
+static unsigned int hash_lwcstring(void *key);
+
+
+/* The operation set for namednodemap */
+static dom_exception attributes_get_length(void *priv,
+ unsigned long *length);
+static dom_exception attributes_get_named_item(void *priv,
+ struct dom_string *name, struct dom_node **node);
+static dom_exception attributes_set_named_item(void *priv,
+ struct dom_node *arg, struct dom_node **node);
+static dom_exception attributes_remove_named_item(
+ void *priv, struct dom_string *name,
+ struct dom_node **node);
+static dom_exception attributes_item(void *priv,
+ unsigned long index, struct dom_node **node);
+static dom_exception attributes_get_named_item_ns(
+ void *priv, struct dom_string *namespace,
+ struct dom_string *localname, struct dom_node **node);
+static dom_exception attributes_set_named_item_ns(
+ void *priv, struct dom_node *arg,
+ struct dom_node **node);
+static dom_exception attributes_remove_named_item_ns(
+ void *priv, struct dom_string *namespace,
+ struct dom_string *localname, struct dom_node **node);
+static void attributes_destroy(void *priv);
+static bool attributes_equal(void *p1, void *p2);
+
+static struct nnm_operation attributes_opt = {
+ attributes_get_length,
+ attributes_get_named_item,
+ attributes_set_named_item,
+ attributes_remove_named_item,
+ attributes_item,
+ attributes_get_named_item_ns,
+ attributes_set_named_item_ns,
+ attributes_remove_named_item_ns,
+ attributes_destroy,
+ attributes_equal
+};
+
+static dom_exception attr_clone(struct hash_table *sh,
+ struct dom_document *sd, struct hash_table **th,
+ struct dom_document *td, dom_node_internal *parent);
+static void *_key(void *key, void *key_pw, dom_alloc alloc, void *pw,
+ bool clone);
+static void *_value(void *value, void *value_pw, dom_alloc alloc,
+ void *pw, bool clone);
+static void *_nsattributes(void *value, void *value_pw, dom_alloc alloc,
+ void *pw, bool clone);
+/* */
+/*----------------------------------------------------------------------*/
+/* */
+/* Constructors and Destructors */
+
/**
- * Initialise an element node
+ * Create an element node
*
* \param doc The owning document
* \param name The (local) name of the node to create
@@ -43,41 +142,28 @@
*
* The returned element will already be referenced.
*/
-dom_exception dom_element_initialise(struct dom_element *el,
- struct dom_string *name, struct dom_string *namespace,
- struct dom_string *prefix, struct dom_element **result)
+dom_exception dom_element_create(struct dom_document *doc,
+ struct lwc_string_s *name, struct lwc_string_s *namespace,
+ struct lwc_string_s *prefix, struct dom_element **result)
{
- dom_exception err;
- struct dom_document *doc;
+ struct dom_element *el;
- dom_node_get_owner_document(el, &doc);
+ /* Allocate the element */
+ el = dom_document_alloc(doc, NULL, sizeof(struct dom_element));
+ if (el == NULL)
+ return DOM_NO_MEM_ERR;
- /** \todo Sanity check the tag name */
-
- /* Initialise the base class */
- err = dom_node_initialise(&el->base, doc, DOM_ELEMENT_NODE,
- name, NULL, namespace, prefix);
- if (err != DOM_NO_ERR) {
- dom_document_alloc(doc, el, 0);
- return err;
- }
-
- /* Perform our type-specific initialisation */
- el->attributes = NULL;
- el->schema_type_info = NULL;
-
/* Init the vtable's destroy function */
el->base.base.vtable = &element_vtable;
- el->base.destroy = &_dom_element_destroy;
+ el->base.vtable = &element_protect_vtable;
- *result = el;
-
- return DOM_NO_ERR;
+ return dom_element_initialise(el, doc, name, namespace, prefix, result);
}
/**
- * Create an element node
+ * Initialise an element node
*
+ * \param el The element
* \param doc The owning document
* \param name The (local) name of the node to create
* \param namespace The namespace URI of the element, or NULL
@@ -92,19 +178,45 @@
*
* The returned element will already be referenced.
*/
-dom_exception dom_element_create(struct dom_document *doc,
- struct dom_string *name, struct dom_string *namespace,
- struct dom_string *prefix, struct dom_element **result)
+dom_exception dom_element_initialise(struct dom_element *el,
+ struct dom_document *doc, struct lwc_string_s *name,
+ struct lwc_string_s *namespace, struct lwc_string_s *prefix,
+ struct dom_element **result)
{
- struct dom_element *el;
+ dom_exception err;
- /* Allocate the element */
- el = dom_document_alloc(doc, NULL, sizeof(struct dom_element));
- if (el == NULL)
- return DOM_NO_MEM_ERR;
+ assert(doc != NULL);
+ /** \todo Sanity check the tag name */
- dom_element_initialise(el, name, namespace, prefix, result);
-
+ err = dom_document_create_hashtable(doc, CHAINS_ATTRIBUTES,
+ hash_lwcstring, &el->attributes);
+ if (err != DOM_NO_ERR) {
+ dom_document_alloc(doc, el, 0);
+ return err;
+ }
+
+ err = dom_document_create_hashtable(doc, CHAINS_NAMESPACE,
+ hash_lwcstring, &el->ns_attributes);
+ if (err != DOM_NO_ERR) {
+ dom_document_alloc(doc, el, 0);
+ dom_document_alloc(doc, el->attributes, 0);
+ return err;
+ }
+ /* Initialise the base class */
+ err = dom_node_initialise(&el->base, doc, DOM_ELEMENT_NODE,
+ name, NULL, namespace, prefix);
+ if (err != DOM_NO_ERR) {
+ dom_document_alloc(doc, el, 0);
+ return err;
+ }
+
+ /* Perform our type-specific initialisation */
+ el->id_ns = NULL;
+ el->id_name = NULL;
+ el->schema_type_info = NULL;
+
+ *result = el;
+
return DOM_NO_ERR;
}
@@ -119,51 +231,18 @@
void dom_element_destroy(struct dom_document *doc,
struct dom_element *element)
{
- struct dom_node_internal *c, *d;
+ lwc_context *ctx = dom_document_get_intern_context(doc);
+ assert (ctx != NULL);
- /* Destroy children of this node */
- for (c = element->base.first_child; c != NULL; c = d) {
- d = c->next;
-
- /* Detach child */
- c->parent = NULL;
-
- if (c->refcnt > 0) {
- /* Something is using this child */
-
- /** \todo add to list of nodes pending deletion */
-
- continue;
- }
-
- /* Detach from sibling list */
- c->previous = NULL;
- c->next = NULL;
-
- dom_node_destroy(c);
+ /* Destroy attributes attached to this node */
+ if (element->attributes != NULL) {
+ hash_destroy(element->attributes, _key, ctx, _value, ctx);
+ element->attributes = NULL;
}
- /* Destroy attributes attached to this node */
- for (c = (struct dom_node_internal *) element->attributes;
- c != NULL; c = d) {
- d = c->next;
-
- /* Detach child */
- c->parent = NULL;
-
- if (c->refcnt > 0) {
- /* Something is using this attribute */
-
- /** \todo add to list of nodes pending deletion */
-
- continue;
- }
-
- /* Detach from sibling list */
- c->previous = NULL;
- c->next = NULL;
-
- dom_node_destroy(c);
+ if (element->ns_attributes != NULL) {
+ hash_destroy(element->ns_attributes, _key, ctx, _nsattributes, ctx);
+ element->ns_attributes = NULL;
}
if (element->schema_type_info != NULL) {
@@ -177,19 +256,11 @@
dom_document_alloc(doc, element, 0);
}
-/**
- * The destroy virtual function of dom_element
- *
- * \param element The element to be destroyed
- **/
-void _dom_element_destroy(struct dom_node_internal *node)
-{
- struct dom_document *doc;
- dom_node_get_owner_document(node, &doc);
+/* */
+/*----------------------------------------------------------------------*/
+/* */
+/* The public virtual functions */
- dom_element_destroy(doc, (struct dom_element *) node);
-}
-
/**
* Retrieve an element's tag name
*
@@ -224,23 +295,7 @@
dom_exception _dom_element_get_attribute(struct dom_element *element,
struct dom_string *name, struct dom_string **value)
{
- struct dom_node_internal *a = (struct dom_node_internal *)
- element->attributes;
-
- /* Search attributes, looking for name */
- for (; a != NULL; a = a->next) {
- if (dom_string_cmp(a->name, name) == 0)
- break;
- }
-
- /* Fill in value */
- if (a == NULL) {
- *value = NULL;
- } else {
- dom_attr_get_value(((struct dom_attr *) a), value);
- }
-
- return DOM_NO_ERR;
+ return _dom_element_get_attr(element, element->attributes, name, value);
}
/**
@@ -256,58 +311,7 @@
dom_exception _dom_element_set_attribute(struct dom_element *element,
struct dom_string *name, struct dom_string *value)
{
- struct dom_node_internal *e = (struct dom_node_internal *) element;
- struct dom_node_internal *a = (struct dom_node_internal *)
- element->attributes;
-
- /** \todo validate name */
-
- /* Ensure element can be written to */
- if (_dom_node_readonly(e))
- return DOM_NO_MODIFICATION_ALLOWED_ERR;
-
- /* Search for existing attribute with same name */
- for (; a != NULL; a = a->next) {
- if (dom_string_cmp(a->name, name) == 0)
- break;
- }
-
- if (a != NULL) {
- /* Found an existing attribute, so replace its value */
- dom_exception err;
-
- err = dom_attr_set_value((struct dom_attr *) a, value);
- if (err != DOM_NO_ERR)
- return err;
- } else {
- /* No existing attribute, so create one */
- dom_exception err;
- struct dom_attr *attr;
-
- err = dom_attr_create(e->owner, name, NULL, NULL, &attr);
- if (err != DOM_NO_ERR)
- return err;
-
- /* Set its value */
- err = dom_attr_set_value(attr, value);
- if (err != DOM_NO_ERR) {
- dom_node_unref((struct dom_node *) attr);
- return err;
- }
-
- a = (struct dom_node_internal *) attr;
-
- /* And insert it into the element */
- a->previous = NULL;
- a->next = (struct dom_node_internal *) element->attributes;
-
- if (a->next != NULL)
- a->next->previous = a;
-
- element->attributes = attr;
- }
-
- return DOM_NO_ERR;
+ return _dom_element_set_attr(element, element->attributes, name, value);
}
/**
@@ -321,39 +325,7 @@
dom_exception _dom_element_remove_attribute(struct dom_element *element,
struct dom_string *name)
{
- struct dom_node_internal *e = (struct dom_node_internal *) element;
- struct dom_node_internal *a = (struct dom_node_internal *)
- element->attributes;
-
- /* Ensure element can be written to */
- if (_dom_node_readonly(e))
- return DOM_NO_MODIFICATION_ALLOWED_ERR;
-
- /* Search for existing attribute with same name */
- for (; a != NULL; a = a->next) {
- if (dom_string_cmp(a->name, name) == 0)
- break;
- }
-
- /* Detach attr node from list */
- if (a != NULL) {
- if (a->previous != NULL)
- a->previous->next = a->next;
- else
- element->attributes = (struct dom_attr *) a->next;
-
- if (a->next != NULL)
- a->next->previous = a->previous;
-
- a->previous = a->next = a->parent = NULL;
-
- /* And destroy attr */
- dom_node_unref(a);
- }
-
- /** \todo defaulted attribute handling */
-
- return DOM_NO_ERR;
+ return _dom_element_remove_attr(element, element->attributes, name);
}
/**
@@ -368,23 +340,11 @@
* the responsibility of the caller to unref the node once it has
* finished with it.
*/
-dom_exception _dom_element_get_attribute_node(struct dom_element *element,
+dom_exception _dom_element_get_attribute_node(struct dom_element *element,
struct dom_string *name, struct dom_attr **result)
{
- struct dom_node_internal *a = (struct dom_node_internal *)
- element->attributes;
-
- /* Search attributes, looking for name */
- for (; a != NULL; a = a->next) {
- if (dom_string_cmp(a->name, name) == 0)
- break;
- }
-
- if (a != NULL)
- dom_node_ref(a);
- *result = (struct dom_attr *) a;
-
- return DOM_NO_ERR;
+ return _dom_element_get_attr_node(element, element->attributes, name,
+ result);
}
/**
@@ -407,78 +367,8 @@
dom_exception _dom_element_set_attribute_node(struct dom_element *element,
struct dom_attr *attr, struct dom_attr **result)
{
- struct dom_node_internal *e = (struct dom_node_internal *) element;
- struct dom_node_internal *a = (struct dom_node_internal *) attr;
- struct dom_attr *prev = NULL;
-
- /* Ensure element and attribute belong to the same document */
- if (e->owner != a->owner)
- return DOM_WRONG_DOCUMENT_ERR;
-
- /* Ensure element can be written to */
- if (_dom_node_readonly(e))
- return DOM_NO_MODIFICATION_ALLOWED_ERR;
-
- /* Ensure attribute isn't attached to another element */
- if (a->parent != NULL && a->parent != e)
- return DOM_INUSE_ATTRIBUTE_ERR;
-
- /* Attach attr to element, if not already attached */
- if (a->parent == NULL) {
-
- /* Search for existing attribute with same name */
- prev = element->attributes;
- while (prev != NULL) {
- struct dom_node_internal *p =
- (struct dom_node_internal *) prev;
-
- if (dom_string_cmp(a->name, p->name) == 0)
- break;
-
- prev = (struct dom_attr *) p->next;
- }
-
- a->parent = e;
-
- if (prev != NULL) {
- /* Found an existing attribute, so replace it */
- struct dom_node_internal *p =
- (struct dom_node_internal *) prev;
-
- a->previous = p->previous;
- a->next = p->next;
-
- if (a->previous != NULL)
- a->previous->next = a;
- else
- element->attributes = attr;
-
- if (a->next != NULL)
- a->next->previous = a;
-
- /* Invalidate existing attribute's location info */
- p->next = NULL;
- p->previous = NULL;
- p->parent = NULL;
- } else {
- /* No existing attribute, so insert at front of list */
- a->previous = NULL;
- a->next = (struct dom_node_internal *)
- element->attributes;
-
- if (a->next != NULL)
- a->next->previous = a;
-
- element->attributes = attr;
- }
- }
-
- if (prev != NULL)
- dom_node_ref((struct dom_node *) prev);
-
- *result = prev;
-
- return DOM_NO_ERR;
+ return _dom_element_set_attr_node(element, element->attributes, attr,
+ result);
}
/**
@@ -499,35 +389,8 @@
dom_exception _dom_element_remove_attribute_node(struct dom_element *element,
struct dom_attr *attr, struct dom_attr **result)
{
- struct dom_node_internal *e = (struct dom_node_internal *) element;
- struct dom_node_internal *a = (struct dom_node_internal *) attr;
-
- /* Ensure element can be written to */
- if (_dom_node_readonly(e))
- return DOM_NO_MODIFICATION_ALLOWED_ERR;
-
- /* Ensure attr is an attribute of element */
- if (a->parent != e)
- return DOM_NOT_FOUND_ERR;
-
- /* Detach attr node from list */
- if (a->previous != NULL)
- a->previous->next = a->next;
- else
- element->attributes = (struct dom_attr *) a->next;
-
- if (a->next != NULL)
- a->next->previous = a->previous;
-
- a->previous = a->next = a->parent = NULL;
-
- /** \todo defaulted attribute handling */
-
- /* Return the detached node */
- dom_node_ref(a);
- *result = attr;
-
- return DOM_NO_ERR;
+ return _dom_element_remove_attr_node(element, element->attributes,
+ attr, result);
}
/**
@@ -547,9 +410,25 @@
struct dom_element *element, struct dom_string *name,
struct dom_nodelist **result)
{
- return dom_document_get_nodelist(element->base.owner,
- (struct dom_node_internal *) element, name, NULL,
+ dom_exception err;
+ lwc_string *n;
+ lwc_context *ctx;
+ dom_node_internal *base = (dom_node_internal *) element;
+
+ assert(base->owner != NULL);
+ ctx = dom_document_get_intern_context(base->owner);
+ assert(ctx != NULL);
+
+ err = dom_string_intern(name, ctx, &n);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ err = dom_document_get_nodelist(base->owner, DOM_NODELIST_BY_NAME,
+ (struct dom_node_internal *) element, n, NULL,
NULL, result);
+
+ lwc_context_string_unref(ctx, n);
+ return err;
}
/**
@@ -573,28 +452,25 @@
struct dom_string *namespace, struct dom_string *localname,
struct dom_string **value)
{
- struct dom_node_internal *a = (struct dom_node_internal *)
- element->attributes;
+ lwc_string *str;
+ dom_exception err;
+ struct hash_table *attrs;
- /** \todo ensure implementation supports XML */
+ if (namespace == NULL)
+ return _dom_element_get_attribute(element, localname, value);
- /* Search attributes, looking for namespace/localname pair */
- for (; a != NULL; a = a->next) {
- if (((namespace == NULL && a->namespace == NULL) ||
- (namespace != NULL &&
- dom_string_cmp(a->namespace, namespace) == 0)) &&
- dom_string_cmp(a->name, localname) == 0)
- break;
- }
-
- /* Fill in value */
- if (a == NULL) {
+ err = dom_node_get_intern_string(&element->base, namespace, &str);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ attrs = (struct hash_table *) hash_get(element->ns_attributes, str);
+ /* The element has no such namespace */
+ if (attrs == NULL) {
*value = NULL;
- } else {
- dom_attr_get_value(((struct dom_attr *) a), value);
+ return DOM_NO_ERR;
}
- return DOM_NO_ERR;
+ return _dom_element_get_attr(element, attrs, localname, value);
}
/**
@@ -630,101 +506,54 @@
struct dom_string *namespace, struct dom_string *qname,
struct dom_string *value)
{
- struct dom_node_internal *e = (struct dom_node_internal *) element;
- struct dom_node_internal *a = (struct dom_node_internal *)
- element->attributes;
- struct dom_string *prefix, *localname;
+ lwc_string *str;
dom_exception err;
+ struct hash_table *attrs;
+ bool added;
- /** \todo ensure XML feature is supported */
+ if (_dom_validate_name(qname) == false)
+ return DOM_INVALID_CHARACTER_ERR;
- /* Validate name */
err = _dom_namespace_validate_qname(qname, namespace);
- if (err != DOM_NO_ERR) {
- return err;
- }
+ if (err != DOM_NO_ERR)
+ return DOM_NAMESPACE_ERR;
- /* Ensure element can be written to */
- if (_dom_node_readonly(e)) {
- return DOM_NO_MODIFICATION_ALLOWED_ERR;
- }
-
- /* Decompose QName */
+ dom_string *localname;
+ dom_string *prefix;
err = _dom_namespace_split_qname(qname, &prefix, &localname);
- if (err != DOM_NO_ERR) {
+ if (err != DOM_NO_ERR)
return err;
- }
- /* Search for existing attribute with same namespace/localname */
- for (; a != NULL; a = a->next) {
- if (((namespace == NULL && a->namespace == NULL) ||
- (namespace != NULL &&
- dom_string_cmp(a->namespace, namespace) == 0)) &&
- dom_string_cmp(a->name, localname) == 0)
- break;
+ /* If there is no namespace, redirect to set_attribute */
+ if (namespace == NULL) {
+ if (prefix != NULL)
+ return DOM_NAMESPACE_ERR;
+ err = _dom_element_set_attribute(element, localname, value);
+ dom_string_unref(localname);
+ return err;
}
- if (a != NULL) {
- /* Found an existing attribute, so replace its prefix & value */
- dom_exception err;
-
- err = dom_node_set_prefix(a, prefix);
- if (err != DOM_NO_ERR) {
- if (prefix != NULL) {
- dom_string_unref(prefix);
- }
- dom_string_unref(localname);
+ err = dom_node_get_intern_string(&element->base, namespace, &str);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ attrs = (struct hash_table *) hash_get(element->ns_attributes, str);
+ /* The element has no such namespace */
+ if (attrs == NULL) {
+ dom_document *doc;
+ doc = dom_node_get_owner(element);
+ assert(doc != NULL);
+ err = dom_document_create_hashtable(doc, CHAINS_NS_ATTRIBUTES,
+ hash_lwcstring, &attrs);
+ if (err != DOM_NO_ERR)
return err;
- }
- err = dom_attr_set_value((struct dom_attr *) a, value);
- if (err != DOM_NO_ERR) {
- if (prefix != NULL) {
- dom_string_unref(prefix);
- }
- dom_string_unref(localname);
- return err;
- }
- } else {
- /* No existing attribute, so create one */
- dom_exception err;
- struct dom_attr *attr;
-
- err = dom_attr_create(e->owner, localname,
- namespace, prefix, &attr);
- if (err != DOM_NO_ERR) {
- if (prefix != NULL) {
- dom_string_unref(prefix);
- }
- dom_string_unref(localname);
- return err;
- }
-
- /* Set its value */
- err = dom_attr_set_value(attr, value);
- if (err != DOM_NO_ERR) {
- dom_node_unref((struct dom_node *) attr);
-
- if (prefix != NULL) {
- dom_string_unref(prefix);
- }
- dom_string_unref(localname);
- return err;
- }
-
- a = (struct dom_node_internal *) attr;
-
- /* And insert it into the element */
- a->previous = NULL;
- a->next = (struct dom_node_internal *) element->attributes;
-
- if (a->next != NULL)
- a->next->previous = a;
-
- element->attributes = attr;
+ added = hash_add(element->ns_attributes, str, attrs, false);
+ if (added == false)
+ return DOM_NO_MEM_ERR;
}
- return DOM_NO_ERR;
+ return _dom_element_set_attr(element, attrs, localname, value);
}
/**
@@ -744,44 +573,24 @@
dom_exception _dom_element_remove_attribute_ns(struct dom_element *element,
struct dom_string *namespace, struct dom_string *localname)
{
- struct dom_node_internal *e = (struct dom_node_internal *) element;
- struct dom_node_internal *a = (struct dom_node_internal *)
- element->attributes;
+ lwc_string *str;
+ dom_exception err;
+ struct hash_table *attrs;
- /** \todo ensure XML feature is supported */
+ if (namespace != NULL)
+ return _dom_element_remove_attribute(element, localname);
- /* Ensure element can be written to */
- if (_dom_node_readonly(e))
- return DOM_NO_MODIFICATION_ALLOWED_ERR;
-
- /* Search for existing attribute with same namespace/localname */
- for (; a != NULL; a = a->next) {
- if (((namespace == NULL && a->namespace == NULL) ||
- (namespace != NULL &&
- dom_string_cmp(a->namespace, namespace) == 0)) &&
- dom_string_cmp(a->name, localname) == 0)
- break;
+ err = dom_node_get_intern_string(&element->base, namespace, &str);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ attrs = (struct hash_table *) hash_get(element->ns_attributes, str);
+ /* The element has no such namespace */
+ if (attrs == NULL) {
+ return DOM_NO_ERR;
}
- /* Detach attr node from list */
- if (a != NULL) {
- if (a->previous != NULL)
- a->previous->next = a->next;
- else
- element->attributes = (struct dom_attr *) a->next;
-
- if (a->next != NULL)
- a->next->previous = a->previous;
-
- a->previous = a->next = a->parent = NULL;
-
- /* And destroy attr */
- dom_node_unref(a);
- }
-
- /** \todo defaulted attribute handling */
-
- return DOM_NO_ERR;
+ return _dom_element_remove_attr(element, attrs, localname);
}
/**
@@ -805,25 +614,26 @@
struct dom_string *namespace, struct dom_string *localname,
struct dom_attr **result)
{
- struct dom_node_internal *a = (struct dom_node_internal *)
- element->attributes;
+ lwc_string *str;
+ dom_exception err;
+ struct hash_table *attrs;
- /** \todo ensure XML feature is supported */
+ if (namespace == NULL) {
+ return _dom_element_get_attribute_node(element, localname, result);
+ }
- /* Search attributes, looking for namespace/localname */
- for (; a != NULL; a = a->next) {
- if (((namespace == NULL && a->namespace == NULL) ||
- (namespace != NULL &&
- dom_string_cmp(a->namespace, namespace) == 0)) &&
- dom_string_cmp(a->name, localname) == 0)
- break;
+ err = dom_node_get_intern_string(&element->base, namespace, &str);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ attrs = (struct hash_table *) hash_get(element->ns_attributes, str);
+ /* The element has no such namespace */
+ if (attrs == NULL) {
+ *result = NULL;
+ return DOM_NO_ERR;
}
- if (a != NULL)
- dom_node_ref(a);
- *result = (struct dom_attr *) a;
-
- return DOM_NO_ERR;
+ return _dom_element_get_attr_node(element, attrs, localname, result);
}
/**
@@ -851,84 +661,45 @@
dom_exception _dom_element_set_attribute_node_ns(struct dom_element *element,
struct dom_attr *attr, struct dom_attr **result)
{
- struct dom_node_internal *e = (struct dom_node_internal *) element;
- struct dom_node_internal *a = (struct dom_node_internal *) attr;
- struct dom_attr *prev = NULL;
+ lwc_string *str;
+ dom_exception err;
+ struct hash_table *attrs;
+ bool added;
+ dom_string *namespace;
- /** \todo ensure XML feature is supported */
+ err = dom_node_get_namespace(attr, (void *) &namespace);
+ if (err != DOM_NO_ERR)
+ return err;
- /* Ensure element and attribute belong to the same document */
- if (e->owner != a->owner)
- return DOM_WRONG_DOCUMENT_ERR;
+ if (namespace == NULL)
+ return _dom_element_set_attribute_node(element, attr, result);
- /* Ensure element can be written to */
- if (_dom_node_readonly(e))
- return DOM_NO_MODIFICATION_ALLOWED_ERR;
+ err = dom_node_get_intern_string(&element->base, namespace, &str);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ attrs = (struct hash_table *) hash_get(element->ns_attributes, str);
+ /* The element has no such namespace */
+ if (attrs == NULL) {
+ dom_document *doc;
+ doc = dom_node_get_owner(element);
+ assert(doc != NULL);
+ err = dom_document_create_hashtable(doc, CHAINS_NS_ATTRIBUTES,
+ hash_lwcstring, &attrs);
+ if (err != DOM_NO_ERR)
+ return err;
- /* Ensure attribute isn't attached to another element */
- if (a->parent != NULL && a->parent != e)
- return DOM_INUSE_ATTRIBUTE_ERR;
-
- /* Attach attr to element, if not already attached */
- if (a->parent == NULL) {
-
- /* Search for existing attribute with same namespace/localname */
- prev = element->attributes;
- while (prev != NULL) {
- struct dom_node_internal *p =
- (struct dom_node_internal *) prev;
-
- if (((a->namespace == NULL && p->namespace == NULL) ||
- (a->namespace != NULL &&
- dom_string_cmp(a->namespace,
- p->namespace) == 0)) &&
- dom_string_cmp(a->name, p->name) == 0)
- break;
-
- prev = (struct dom_attr *) p->next;
- }
-
- a->parent = e;
-
- if (prev != NULL) {
- /* Found an existing attribute, so replace it */
- struct dom_node_internal *p =
- (struct dom_node_internal *) prev;
-
- a->previous = p->previous;
- a->next = p->next;
-
- if (a->previous != NULL)
- a->previous->next = a;
- else
- element->attributes = attr;
-
- if (a->next != NULL)
- a->next->previous = a;
-
- /* Invalidate existing attribute's location info */
- p->next = NULL;
- p->previous = NULL;
- p->parent = NULL;
- } else {
- /* No existing attribute, so insert at front of list */
- a->previous = NULL;
- a->next = (struct dom_node_internal *)
- element->attributes;
-
- if (a->next != NULL)
- a->next->previous = a;
-
- element->attributes = attr;
- }
+ added = hash_add(element->ns_attributes, str, attrs, false);
+ if (added == false)
+ return DOM_NO_MEM_ERR;
}
- if (prev != NULL)
- dom_node_ref((struct dom_node *) prev);
+ dom_string *localname;
+ err = dom_node_get_local_name(attr, (void *) &localname);
+ if (err != DOM_NO_ERR)
+ return err;
- *result = prev;
-
- return DOM_NO_ERR;
+ return _dom_element_set_attr_node(element, attrs, attr, result);
}
/**
@@ -953,11 +724,39 @@
struct dom_element *element, struct dom_string *namespace,
struct dom_string *localname, struct dom_nodelist **result)
{
+ dom_document *doc;
+ dom_exception err;
+ doc = element->base.owner;
+
/** \todo ensure XML feature is supported */
- return dom_document_get_nodelist(element->base.owner,
+ /* Get the interned string from the dom_string */
+ assert(doc->context != NULL);
+ lwc_string *l = NULL, *n = NULL;
+ if (localname != NULL) {
+ err = dom_string_intern(localname, doc->context, &l);
+ if (err != DOM_NO_ERR)
+ return err;
+ }
+ if (namespace != NULL) {
+ err = dom_string_intern(namespace, doc->context, &n);
+ if (err != DOM_NO_ERR) {
+ lwc_context_string_unref(doc->context, l);
+
+ return err;
+ }
+ }
+
+ err = dom_document_get_nodelist(element->base.owner, DOM_NODELIST_BY_NAMESPACE,
(struct dom_node_internal *) element, NULL,
- namespace, localname, result);
+ n, l, result);
+
+ if (localname != NULL)
+ lwc_context_string_unref(doc->context, l);
+ if (namespace != NULL)
+ lwc_context_string_unref(doc->context, n);
+
+ return err;
}
/**
@@ -971,18 +770,7 @@
dom_exception _dom_element_has_attribute(struct dom_element *element,
struct dom_string *name, bool *result)
{
- struct dom_node_internal *a = (struct dom_node_internal *)
- element->attributes;
-
- /* Search attributes, looking for name */
- for (; a != NULL; a = a->next) {
- if (dom_string_cmp(a->name, name) == 0)
- break;
- }
-
- *result = (a != NULL);
-
- return DOM_NO_ERR;
+ return _dom_element_has_attr(element, element->attributes, name, result);
}
/**
@@ -1003,23 +791,25 @@
struct dom_string *namespace, struct dom_string *localname,
bool *result)
{
- struct dom_node_internal *a = (struct dom_node_internal *)
- element->attributes;
+ lwc_string *str;
+ dom_exception err;
+ struct hash_table *attrs;
- /** \todo ensure XML feature is supported */
+ if (namespace == NULL)
+ return _dom_element_has_attribute(element, localname, result);
- /* Search attributes, looking for namespace/localname */
- for (; a != NULL; a = a->next) {
- if (((namespace == NULL && a->namespace == NULL) ||
- (namespace != NULL &&
- dom_string_cmp(a->namespace, namespace) == 0)) &&
- dom_string_cmp(a->name, localname) == 0)
- break;
+ err = dom_node_get_intern_string(&element->base, namespace, &str);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ attrs = (struct hash_table *) hash_get(element->ns_attributes, str);
+ /* The element has no such namespace */
+ if (attrs == NULL) {
+ *result = false;
+ return DOM_NO_ERR;
}
- *result = (a != NULL);
-
- return DOM_NO_ERR;
+ return _dom_element_has_attr(element, attrs, localname, result);
}
/**
@@ -1052,15 +842,16 @@
* DOM_NO_MODIFICATION_ALLOWED_ERR if ::element is readonly,
* DOM_NOT_FOUND_ERR if the specified node is not an
* attribute of ::element.
+ *
+ * @note: The DOM spec does not say: how to deal with when there are two or
+ * more isId attribute nodes. Here, the implementation just maintain only
+ * one such attribute node.
*/
dom_exception _dom_element_set_id_attribute(struct dom_element *element,
struct dom_string *name, bool is_id)
{
- UNUSED(element);
- UNUSED(name);
- UNUSED(is_id);
-
- return DOM_NOT_SUPPORTED_ERR;
+ return _dom_element_set_id_attr(element, element->attributes, name,
+ is_id);
}
/**
@@ -1079,12 +870,27 @@
struct dom_string *namespace, struct dom_string *localname,
bool is_id)
{
- UNUSED(element);
- UNUSED(namespace);
- UNUSED(localname);
- UNUSED(is_id);
+ struct hash_table *hs;
+ dom_exception err;
+ lwc_string *ns;
- return DOM_NOT_SUPPORTED_ERR;
+ if (namespace == NULL)
+ return _dom_element_set_id_attribute(element, localname, is_id);
+
+ err = dom_node_get_intern_string(&element->base, namespace, &ns);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ hs = (struct hash_table *) hash_get(element->ns_attributes, ns);
+ assert(hs != NULL);
+
+ err = _dom_element_set_id_attr(element, hs, localname, is_id);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ element->id_ns = ns;
+
+ return DOM_NO_ERR;
}
/**
@@ -1101,59 +907,1144 @@
dom_exception _dom_element_set_id_attribute_node(struct dom_element *element,
struct dom_attr *id_attr, bool is_id)
{
- UNUSED(element);
- UNUSED(id_attr);
- UNUSED(is_id);
+ struct hash_table *hs;
+ dom_exception err;
+ lwc_string *ns;
+ dom_string *namespace;
+ dom_string *localname;
- return DOM_NOT_SUPPORTED_ERR;
+ err = dom_node_get_namespace(id_attr, &namespace);
+ if (err != DOM_NO_ERR)
+ return err;
+ err = dom_node_get_local_name(id_attr, &localname);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ err = dom_node_get_intern_string(&element->base, namespace, &ns);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ hs = (struct hash_table *) hash_get(element->ns_attributes, ns);
+ assert(hs != NULL);
+
+ err = _dom_element_set_id_attr(element, hs, localname, is_id);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ element->id_ns = ns;
+
+ return DOM_NO_ERR;
+
}
+/*------------- The overload virtual functions ------------------------*/
+dom_exception _dom_element_get_attributes(dom_node_internal *node,
+ struct dom_namednodemap **result)
+{
+ dom_exception err;
+ dom_document *doc;
+
+ doc = dom_node_get_owner(node);
+ assert(doc != NULL);
+
+ err = dom_namednodemap_create(doc, node, &attributes_opt, result);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ dom_node_ref(node);
+
+ return DOM_NO_ERR;
+}
+
+
+dom_exception _dom_element_has_attributes(dom_node_internal *node, bool *result)
+{
+ UNUSED(node);
+ *result = true;
+
+ return DOM_NO_ERR;
+}
+
+/* For the following namespace related algorithm take a look at:
+ * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/namespaces-algori...
+ */
+
+/**
+ * Look up the prefix which match the namespace.
+ *
+ * \param node The current Node in which we search for
+ * \param namespace The namespace for which we search a prefix
+ * \param result The returned prefix
+ */
+dom_exception _dom_element_lookup_prefix(dom_node_internal *node,
+ struct dom_string *namespace, struct dom_string **result)
+{
+ struct dom_element *owner;
+ dom_exception err;
+
+ err = dom_attr_get_owner_element(node, &owner);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ if (owner == NULL) {
+ *result = NULL;
+ return DOM_NO_ERR;
+ }
+
+ return dom_node_lookup_prefix(owner, namespace, result);
+}
+
+/**
+ * Test whether certain namespace is the default namespace of some node.
+ *
+ * \param node The Node to test
+ * \param namespace The namespace to test
+ * \param result true is the namespace is default namespace
+ */
+dom_exception _dom_element_is_default_namespace(dom_node_internal *node,
+ struct dom_string *namespace, bool *result)
+{
+ struct dom_element *ele = (struct dom_element *) node;
+ lwc_string *ns;
+ dom_string *value;
+ dom_document *doc = node->owner;
+ lwc_context *ctx;
+ dom_exception err;
+
+ assert(doc != NULL);
+ err = dom_node_get_intern_string(node, namespace, &ns);
+ if (err != DOM_NO_ERR) {
+ return err;
+ }
+ ctx = dom_document_get_intern_context(doc);
+ assert(ctx != NULL);
+ if (node->prefix == NULL) {
+ lwc_context_string_isequal(ctx, node->namespace, ns, result);
+ lwc_context_string_unref(ctx, ns);
+ return DOM_NO_ERR;
+ }
+
+ bool has;
+ dom_string *xmlns = _dom_namespace_get_xmlns_prefix();
+ err = dom_element_has_attribute(ele, xmlns, &has);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ if (has == true) {
+ return dom_element_get_attribute(ele, xmlns, &value);
+ }
+
+ lwc_string *ns2;
+ err = dom_node_get_intern_string(node, value, &ns2);
+ if (err != DOM_NO_ERR) {
+ return err;
+ }
+
+ if (ns2 != NULL) {
+ lwc_context_string_isequal(ctx, ns2, ns, result);
+ lwc_context_string_unref(ctx, ns);
+ lwc_context_string_unref(ctx, ns2);
+ dom_string_unref(value);
+ return DOM_NO_ERR;
+ }
+
+
+ return dom_node_is_default_namespace(node->parent, namespace, result);
+}
+
+/**
+ * Look up the namespace with certain prefix.
+ *
+ * \param node The current node in which we search for the prefix
+ * \param prefix The prefix to search
+ * \param result The result namespace if found
+ */
+dom_exception _dom_element_lookup_namespace(dom_node_internal *node,
+ struct dom_string *prefix, struct dom_string **result)
+{
+ lwc_string *pf;
+ dom_exception err;
+
+ err = dom_node_get_intern_string(node, prefix, &pf);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ /* To some extent, directly compare the two lwc_string pointer
+ * is better */
+ if (node->namespace != NULL && node->prefix == pf) {
+ assert(node->owner != NULL);
+ return dom_document_create_string_from_lwcstring(node->owner, pf,
+ result);
+ }
+
+ bool has;
+ dom_string *xmlns = _dom_namespace_get_xmlns_prefix();
+ err = dom_element_has_attribute_ns(node, dom_namespaces[DOM_NAMESPACE_XMLNS], prefix, &has);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ if (has == true)
+ return dom_element_get_attribute_ns(node, dom_namespaces[DOM_NAMESPACE_XMLNS], prefix, result);
+
+ err = dom_element_has_attribute(node, xmlns, &has);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ if (has == true) {
+ return dom_element_get_attribute(node, xmlns, result);
+ }
+
+ return dom_node_lookup_namespace(node->parent, prefix, result);
+}
+
+
+/* */
+/*----------------------------------------------------------------------*/
+/* */
+/* The protected virtual functions */
+
+/**
+ * The destroy virtual function of dom_element
+ *
+ * \param element The element to be destroyed
+ **/
+void _dom_element_destroy(struct dom_node_internal *node)
+{
+ struct dom_document *doc = dom_node_get_owner(node);
+
+ dom_element_destroy(doc, (struct dom_element *) node);
+}
+
+dom_exception _dom_element_alloc(dom_document *doc, struct dom_node_internal *n,
+ struct dom_node_internal **ret)
+{
+ dom_element *e;
+ UNUSED(n);
+
+ e = dom_document_alloc(doc, NULL, sizeof(struct dom_element));
+ if (e == NULL)
+ return DOM_NO_MEM_ERR;
+
+ *ret = (dom_node_internal *) e;
+ dom_node_set_owner(*ret, doc);
+
+ return DOM_NO_ERR;
+}
+
+/* TODO: How to deal with default attribue:
+ *
+ * Ask a language binding for default attributes.
+ *
+ * So, when we copy a element we copy all its attributes because they
+ * are all specified. For the methods like importNode and adoptNode,
+ * this will make _dom_element_copy can be used in them.
+ */
+dom_exception _dom_element_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old)
+{
+ dom_element *ne = (dom_element *) new;
+ dom_element *oe = (dom_element *) old;
+ dom_document *od, *nd;
+ struct hash_table *ht, *value;
+ lwc_string *key;
+ lwc_context *oc, *nc;
+ unsigned int c1, *c2 = NULL;
+ dom_exception err;
+
+ err = _dom_node_copy(new, old);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ od = dom_node_get_owner(old);
+ nd = dom_node_get_owner(new);
+ assert(od != NULL);
+ assert(nd != NULL);
+
+ oc = dom_document_get_intern_context(od);
+ nc = dom_document_get_intern_context(nd);
+ assert(oc != NULL);
+ assert(nc != NULL);
+
+ /* copy the hash tables */
+ err = attr_clone(oe->attributes, od, &ht, nd, new);
+ if (err != DOM_NO_ERR)
+ return err;
+ ne->attributes = ht;
+
+ err = dom_document_create_hashtable(nd, hash_get_chains(oe->ns_attributes),
+ hash_get_func(oe->ns_attributes), &ht);
+ if (err != DOM_NO_ERR)
+ return err;
+ ne->ns_attributes = ht;
+
+ while ( (key = (lwc_string *) hash_iterate(oe->ns_attributes, &c1, &c2)
+ ) != NULL) {
+ value = (struct hash_table *) hash_get(oe->ns_attributes, key);
+ if (ht != NULL) {
+ lwc_error lerr;
+ lwc_string *nk;
+
+ if (oc != nc) {
+ lerr = lwc_context_intern(nc, lwc_string_data(key),
+ lwc_string_length(key), &nk);
+ if (lerr != lwc_error_ok) {
+ hash_destroy(ht, _key, nc, _nsattributes, nc);
+ return dom_exception_from_lwc_error(lerr);
+ }
+ } else {
+ lwc_context_string_ref(oc, key);
+ nk = key;
+ }
+
+ struct hash_table *h;
+ err = attr_clone(value, od, &h, nd, new);
+ if (err != DOM_NO_ERR) {
+ lwc_context_string_unref(nc, nk);
+ hash_destroy(ht, _key, nc, _nsattributes, nc);
+ return err;
+ }
+ if (hash_add(ht, nk, h, false) == false) {
+ lwc_context_string_unref(nc, nk);
+ hash_destroy(ht, _key, nc, _nsattributes, nc);
+ return DOM_NO_MEM_ERR;
+ }
+ }
+ }
+
+
+ /* TODO: deal with dom_type_info, it get no definition ! */
+
+ return DOM_NO_ERR;
+}
+
+
/* */
/*----------------------------------------------------------------------------*/
/* */
+/* Helper functions */
/**
- * Retrieve a map of attributes associated with an Element
+ * The internal helper function for getAttribute/getAttributeNS.
*
- * \param element The element to retrieve the attributes of
- * \param result Pointer to location to receive attribute map
- * \return DOM_NO_ERR.
+ * \param element The element
+ * \param hs The hash table contains the attributes
+ * \param name The name of the attribute
+ * \param value The value of the attribute
+ */
+dom_exception _dom_element_get_attr(struct dom_element *element,
+ struct hash_table *hs, struct dom_string *name,
+ struct dom_string **value)
+{
+ void *a;
+ dom_exception err;
+ lwc_string *str;
+
+ /* looking for name */
+ err = dom_node_get_intern_string(&element->base, name, &str);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ a = hash_get(hs, str);
+
+ /* Fill in value */
+ if (a == NULL) {
+ *value = NULL;
+ } else {
+ dom_attr_get_value(((struct dom_attr *) a), value);
+ }
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * The internal helper function for setAttribute and setAttributeNS.
*
- * The returned NamedNodeMap will be referenced. It is the responsibility
- * of the caller to unref the map once it has finished with it.
+ * \param element The element
+ * \param hs The attributes' hash table
+ * \param name The name of the new attribute
+ * \param value The value of the new attribute
*/
-dom_exception dom_element_get_attributes(struct dom_element *element,
- struct dom_namednodemap **result)
+dom_exception _dom_element_set_attr(struct dom_element *element,
+ struct hash_table *hs, struct dom_string *name,
+ struct dom_string *value)
{
- return dom_document_get_namednodemap(element->base.owner,
- (struct dom_node_internal *) element,
- DOM_ATTRIBUTE_NODE, result);
+ void *a;
+ dom_exception err;
+ lwc_string *str;
+ bool added;
+ dom_node_internal *e = (dom_node_internal *) element;
+
+ if (_dom_validate_name(name) == false)
+ return DOM_INVALID_CHARACTER_ERR;
+
+ /* Ensure element can be written to */
+ if (_dom_node_readonly(e))
+ return DOM_NO_MODIFICATION_ALLOWED_ERR;
+
+ /* looking for name */
+ err = dom_node_get_intern_string(&element->base, name, &str);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ a = hash_get(hs, str);
+
+ if (a != NULL) {
+ /* Found an existing attribute, so replace its value */
+ dom_exception err;
+
+ err = dom_attr_set_value((struct dom_attr *) a, value);
+ if (err != DOM_NO_ERR)
+ return err;
+ } else {
+ /* No existing attribute, so create one */
+ dom_exception err;
+ struct dom_attr *attr;
+
+ err = dom_attr_create(e->owner, str, NULL, NULL, true, &attr);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ /* Set its value */
+ err = dom_attr_set_value(attr, value);
+ if (err != DOM_NO_ERR) {
+ dom_node_unref(attr);
+ return err;
+ }
+
+ added = hash_add(hs, str, attr, false);
+ if (added == false) {
+ /* If we failed at this step, there must be no memory */
+ dom_node_unref(attr);
+ return DOM_NO_MEM_ERR;
+ }
+
+ dom_node_set_parent(attr, element);
+ dom_node_unref(attr);
+ dom_node_remove_pending(attr);
+ }
+
+ return DOM_NO_ERR;
}
/**
- * Determine if an element has any attributes
+ * Remove an attribute from an element by name
*
- * \param element Element to inspect
- * \param result Pointer to location to receive result
+ * \param element The element to remove attribute from
+ * \param name The name of the attribute to remove
+ * \return DOM_NO_ERR on success,
+ * DOM_NO_MODIFICATION_ALLOWED_ERR if ::element is readonly.
+ */
+dom_exception _dom_element_remove_attr(struct dom_element *element,
+ struct hash_table *hs, struct dom_string *name)
+{
+ void *a;
+ dom_exception err;
+ lwc_string *str;
+ dom_node_internal *e = (dom_node_internal *) element;
+
+ /* Ensure element can be written to */
+ if (_dom_node_readonly(e))
+ return DOM_NO_MODIFICATION_ALLOWED_ERR;
+
+ /* looking for name */
+ err = dom_node_get_intern_string(&element->base, name, &str);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ a = (dom_node_internal *) hash_del(hs, str);
+
+ /* Detach attr node from list */
+ if (a != NULL) {
+ /* And destroy attr */
+ dom_node_set_parent(a, NULL);
+ dom_node_try_destroy(a);
+ }
+
+ /** \todo defaulted attribute handling */
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Retrieve an attribute node from an element by name
+ *
+ * \param element The element to retrieve attribute node from
+ * \param name The attribute's name
+ * \param result Pointer to location to receive attribute node
* \return DOM_NO_ERR.
+ *
+ * The returned node will have its reference count increased. It is
+ * the responsibility of the caller to unref the node once it has
+ * finished with it.
*/
-dom_exception dom_element_has_attributes(struct dom_element *element,
- bool *result)
+dom_exception _dom_element_get_attr_node(struct dom_element *element,
+ struct hash_table *hs, struct dom_string *name,
+ struct dom_attr **result)
{
- *result = (element->attributes != NULL);
+ void *a;
+ dom_exception err;
+ lwc_string *str;
+ /* looking for name */
+ err = dom_node_get_intern_string(&element->base, name, &str);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ a = hash_get(hs, str);
+
+ /* Fill in value */
+ if (a == NULL) {
+ *result = NULL;
+ } else {
+ *result = (dom_attr *) a;
+ dom_node_ref(*result);
+ }
+
return DOM_NO_ERR;
}
/**
- * Retrieve a pointer to the first attribute attached to an element
+ * Set an attribute node on an element, replacing existing node, if present
*
- * \param element The element to retrieve the first attribute from
- * \return Pointer to first attribute, or NULL if none.
+ * \param element The element to add a node to
+ * \param attr The attribute node to add
+ * \param result Pointer to location to receive previous node
+ * \return DOM_NO_ERR on success,
+ * DOM_WRONG_DOCUMENT_ERR if ::attr does not belong to the
+ * same document as ::element,
+ * DOM_NO_MODIFICATION_ALLOWED_ERR if ::element is readonly,
+ * DOM_INUSE_ATTRIBUTE_ERR if ::attr is already an attribute
+ * of another Element node.
+ *
+ * The returned node will have its reference count increased. It is
+ * the responsibility of the caller to unref the node once it has
+ * finished with it.
*/
-struct dom_node_internal *dom_element_get_first_attribute(
- struct dom_element *element)
+dom_exception _dom_element_set_attr_node(struct dom_element *element,
+ struct hash_table *hs, struct dom_attr *attr,
+ struct dom_attr **result)
{
- return (struct dom_node_internal *) element->attributes;
+ dom_exception err;
+ lwc_string *str = NULL;
+ dom_string *name = NULL;
+ bool added;
+ dom_node_internal *e = (dom_node_internal *) element;
+ dom_node_internal *a = (dom_node_internal *) attr;
+
+ /** \todo validate name */
+
+ /* Ensure element and attribute belong to the same document */
+ if (e->owner != a->owner)
+ return DOM_WRONG_DOCUMENT_ERR;
+
+ /* Ensure element can be written to */
+ if (_dom_node_readonly(e))
+ return DOM_NO_MODIFICATION_ALLOWED_ERR;
+
+ /* Ensure attribute isn't attached to another element */
+ if (a->parent != NULL && a->parent != e)
+ return DOM_INUSE_ATTRIBUTE_ERR;
+
+ err = dom_node_get_local_name(attr, &name);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ /* looking for name */
+ err = dom_node_get_intern_string(&element->base, name, &str);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ a = hash_del(hs, str);
+
+ *result = NULL;
+ if (a != NULL) {
+ dom_node_ref(a);
+ *result = (dom_attr *) a;
+ dom_node_set_parent(a, NULL);
+ dom_node_mark_pending(a);
+ }
+
+ added = hash_add(hs, str, attr, false);
+ if (added == false) {
+ /* If we failed at this step, there must be no memory */
+ return DOM_NO_MEM_ERR;
+ }
+ dom_node_set_parent(attr, element);
+ dom_node_remove_pending(attr);
+
+ /* Cleanup */
+ if (name != NULL)
+ dom_string_unref(name);
+
+ return DOM_NO_ERR;
}
+/**
+ * Remove an attribute node from an element
+ *
+ * \param element The element to remove attribute node from
+ * \param attr The attribute node to remove
+ * \param result Pointer to location to receive attribute node
+ * \return DOM_NO_ERR on success,
+ * DOM_NO_MODIFICATION_ALLOWED_ERR if ::element is readonly,
+ * DOM_NOT_FOUND_ERR if ::attr is not an attribute of
+ * ::element.
+ *
+ * The returned node will have its reference count increased. It is
+ * the responsibility of the caller to unref the node once it has
+ * finished with it.
+ */
+dom_exception _dom_element_remove_attr_node(struct dom_element *element,
+ struct hash_table *hs, struct dom_attr *attr,
+ struct dom_attr **result)
+{
+ void *a;
+ dom_exception err;
+ lwc_string *str;
+ dom_string *name;
+ dom_node_internal *e = (dom_node_internal *) element;
+
+ /* Ensure element can be written to */
+ if (_dom_node_readonly(e))
+ return DOM_NO_MODIFICATION_ALLOWED_ERR;
+
+ err = dom_node_get_node_name(attr, &name);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ /* looking for name */
+ err = dom_node_get_intern_string(&element->base, name, &str);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ a = hash_del(hs, str);
+
+ /* Now, cleaup the dom_string and lwc_string */
+ dom_string_unref(name);
+ dom_node_unref_intern_string(&element->base, str);
+
+ /** \todo defaulted attribute handling */
+
+ if (a == NULL || a != (void *) attr) {
+ return DOM_NOT_FOUND_ERR;
+ }
+
+ /* When a Node is removed, it should be destroy. When its refcnt is not
+ * zero, it will be added to the document's deletion pending list.
+ * When a Node is removed, its parent should be NULL, but its owner should
+ * remain to be the document.
+ */
+ dom_node_ref(a);
+ *result = (dom_attr *) a;
+ dom_node_set_parent(a, NULL);
+ dom_node_mark_pending(a);
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Test whether certain attribute is inside the hash table
+ *
+ * \param element The element
+ * \param hs The hash table contains the attributes
+ * \param name The attribute's name
+ * \param result The return value
+ */
+dom_exception _dom_element_has_attr(struct dom_element *element,
+ struct hash_table *hs, struct dom_string *name, bool *result)
+{
+ void *a;
+ dom_exception err;
+ lwc_string *str;
+
+ /* looking for name */
+ err = dom_node_get_intern_string(&element->base, name, &str);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ a = hash_get(hs, str);
+
+ /* Fill in value */
+ if (a == NULL) {
+ *result = false;
+ } else {
+ *result = true;
+ }
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Set/Unset an attribute Node as a ID.
+ *
+ * \param element The element contains the attribute
+ * \param hs The hash table which contains the attribute node
+ * \param name The name of the attribute
+ * \param is_id true for set the node as a ID attribute, false unset it
+ */
+dom_exception _dom_element_set_id_attr(struct dom_element *element,
+ struct hash_table *hs, struct dom_string *name, bool is_id)
+{
+ dom_attr *attr;
+ lwc_string *str;
+ dom_exception err;
+ struct hash_table *oh;
+
+ /* looking for name */
+ err = dom_node_get_intern_string(&element->base, name, &str);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ attr = (dom_attr *) hash_get(hs, str);
+ if (attr == NULL)
+ return DOM_NOT_FOUND_ERR;
+
+ if (is_id == true) {
+ /* Firstly, clear the previous id attribute if there is one */
+ if (element->id_ns != NULL) {
+ assert(element->id_name != NULL);
+ oh = (struct hash_table *) hash_get(element->ns_attributes,
+ element->id_ns);
+ } else {
+ oh = element->attributes;
+ }
+ assert(oh != NULL);
+
+ if (element->id_name != NULL) {
+ attr = (dom_attr *) hash_get(oh, element->id_name);
+ assert(attr != NULL);
+ dom_attr_set_isid(attr, false);
+ }
+ }
+
+ dom_attr_set_isid(attr, is_id);
+
+ element->id_name = str;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Get the ID string of the element
+ *
+ * \param ele The element
+ * \param id The ID of this element
+ */
+dom_exception dom_element_get_id(struct dom_element *ele, lwc_string **id)
+{
+ dom_exception err;
+ dom_string *ret = NULL;
+
+ *id = NULL;
+
+ if (ele->id_ns != NULL && ele->id_name != NULL) {
+ /* There is user specific ID attribute */
+ dom_document *doc;
+ doc = dom_node_get_owner(ele);
+ assert(doc != NULL);
+
+ dom_string *namespace, *name;
+ err = dom_document_create_string_from_lwcstring(doc, ele->id_ns,
+ &namespace);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ err = dom_document_create_string_from_lwcstring(doc,
+ ele->id_name, &name);
+ if (err != DOM_NO_ERR) {
+ dom_string_unref(namespace);
+ return err;
+ }
+
+ err = _dom_element_get_attribute_ns(ele, namespace, name, &ret);
+ if (err != DOM_NO_ERR) {
+ dom_string_unref(namespace);
+ dom_string_unref(name);
+ return err;
+ }
+
+ dom_string_unref(namespace);
+ dom_string_unref(name);
+
+ err = dom_node_get_intern_string((dom_node_internal *) ele, ret, id);
+ dom_string_unref(ret);
+ return err;
+ }
+
+ dom_document *doc;
+ doc = dom_node_get_owner(ele);
+ assert(doc != NULL);
+ dom_string *name;
+
+ if (ele->id_name != NULL) {
+ err = dom_document_create_string_from_lwcstring(doc,
+ ele->id_name, &name);
+ if (err != DOM_NO_ERR) {
+ return err;
+ }
+
+ } else {
+ err = dom_document_create_string(doc, (uint8_t *) ID_ATTRIBUTE,
+ strlen(ID_ATTRIBUTE), &name);
+ if (err != DOM_NO_ERR) {
+ return err;
+ }
+ }
+
+ err = _dom_element_get_attribute(ele, name, &ret);
+ if (err != DOM_NO_ERR) {
+ dom_string_unref(name);
+ return err;
+ }
+
+ dom_string_unref(name);
+
+ if (ret != NULL) {
+ err = dom_node_get_intern_string((dom_node_internal *) ele, ret, id);
+ dom_string_unref(ret);
+ } else {
+ *id = NULL;
+ }
+
+ return err;
+}
+
+
+/* The hash function for attributes and id tables */
+unsigned int hash_lwcstring(void *key)
+{
+ lwc_string *lstr = (lwc_string *) key;
+
+ return lwc_string_hash_value(lstr);
+}
+
+/* -------------- The dom_namednodemap functions ------------------------- */
+
+/**
+ * Implementation function for NamedNodeMap, see core/namednodemap.h for details
+ */
+dom_exception attributes_get_length(void *priv,
+ unsigned long *length)
+{
+ unsigned int ret = 0;
+ unsigned int c1, *c2 = NULL;
+ void *key, *value;
+ dom_element *e = (dom_element *) priv;
+
+ ret += hash_get_length(e->attributes);
+ while( (key = hash_iterate(e->ns_attributes, &c1, &c2)) != NULL) {
+ value = hash_get(e->ns_attributes, key);
+ if (value != NULL) {
+ ret += hash_get_length((struct hash_table *) value);
+ }
+ }
+
+ *length = ret;
+ return DOM_NO_ERR;
+}
+
+/**
+ * Implementation function for NamedNodeMap, see core/namednodemap.h for details
+ */
+dom_exception attributes_get_named_item(void *priv,
+ struct dom_string *name, struct dom_node **node)
+{
+ dom_element *e = (dom_element *) priv;
+
+ return _dom_element_get_attribute_node(e, name, (dom_attr **) node);
+}
+
+/**
+ * Implementation function for NamedNodeMap, see core/namednodemap.h for details
+ */
+dom_exception attributes_set_named_item(void *priv,
+ struct dom_node *arg, struct dom_node **node)
+{
+ dom_element *e = (dom_element *) priv;
+ dom_node_internal *n = (dom_node_internal *) arg;
+
+ if (n->type != DOM_ATTRIBUTE_NODE)
+ return DOM_HIERARCHY_REQUEST_ERR;
+
+ return _dom_element_set_attribute_node(e, (dom_attr *) arg, (dom_attr **) node);
+}
+
+/**
+ * Implementation function for NamedNodeMap, see core/namednodemap.h for details
+ */
+dom_exception attributes_remove_named_item(
+ void *priv, struct dom_string *name,
+ struct dom_node **node)
+{
+ dom_element *e = (dom_element *) priv;
+ dom_exception err;
+
+ err = _dom_element_get_attribute_node(e, name, (dom_attr **) node);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ if (*node == NULL) {
+ return DOM_NOT_FOUND_ERR;
+ }
+
+ return _dom_element_remove_attribute(e, name);
+}
+
+/**
+ * Implementation function for NamedNodeMap, see core/namednodemap.h for details
+ */
+dom_exception attributes_item(void *priv,
+ unsigned long index, struct dom_node **node)
+{
+ struct hash_table *ht = NULL;
+ unsigned int num = index + 1;
+ unsigned int len;
+ dom_element *e = (dom_element *) priv;
+ void *key, *value;
+ unsigned int c1, *c2 = NULL;
+
+ len = hash_get_length(e->attributes);
+ if (num <= len) {
+ ht = e->attributes;
+ } else {
+ num -= len;
+ }
+
+ while( (key = hash_iterate(e->ns_attributes, &c1, &c2)) != NULL) {
+ value = hash_get(e->ns_attributes, key);
+ if (value != NULL) {
+ len = hash_get_length((struct hash_table *) value);
+ if (num <= len) {
+ ht = (struct hash_table *) value;
+ break;
+ } else {
+ num -= len;
+ }
+ }
+ }
+
+ *node = NULL;
+ c2 = NULL;
+ if (ht != NULL)
+ {
+ while( (key = hash_iterate(ht, &c1, &c2)) != NULL) {
+ value = hash_get(ht, key);
+ if (--num == 0) {
+ *node = (dom_node *) value;
+ break;
+ }
+ }
+ }
+
+ if (*node != NULL)
+ dom_node_ref(*node);
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Implementation function for NamedNodeMap, see core/namednodemap.h for details
+ */
+dom_exception attributes_get_named_item_ns(
+ void *priv, struct dom_string *namespace,
+ struct dom_string *localname, struct dom_node **node)
+{
+ dom_element *e = (dom_element *) priv;
+
+ return _dom_element_get_attribute_node_ns(e, namespace, localname, (dom_attr **) node);
+}
+
+/**
+ * Implementation function for NamedNodeMap, see core/namednodemap.h for details
+ */
+dom_exception attributes_set_named_item_ns(
+ void *priv, struct dom_node *arg,
+ struct dom_node **node)
+{
+ dom_element *e = (dom_element *) priv;
+ dom_node_internal *n = (dom_node_internal *) arg;
+
+ if (n->type != DOM_ATTRIBUTE_NODE)
+ return DOM_HIERARCHY_REQUEST_ERR;
+
+ return _dom_element_set_attribute_node_ns(e, (dom_attr *) arg, (dom_attr **) node);
+}
+
+/**
+ * Implementation function for NamedNodeMap, see core/namednodemap.h for details
+ */
+dom_exception attributes_remove_named_item_ns(
+ void *priv, struct dom_string *namespace,
+ struct dom_string *localname, struct dom_node **node)
+{
+ dom_element *e = (dom_element *) priv;
+ dom_exception err;
+
+ err = _dom_element_get_attribute_node_ns(e, namespace, localname, (dom_attr **) node);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ if (*node == NULL) {
+ return DOM_NOT_FOUND_ERR;
+ }
+
+ return _dom_element_remove_attribute_ns(e, namespace, localname);
+}
+
+/**
+ * Implementation function for NamedNodeMap, see core/namednodemap.h for details
+ */
+void attributes_destroy(void *priv)
+{
+ dom_element *e = (dom_element *) priv;
+
+ dom_node_unref(e);
+}
+
+/**
+ * Implementation function for NamedNodeMap, see core/namednodemap.h for details
+ */
+bool attributes_equal(void *p1, void *p2)
+{
+ return p1 == p2;
+}
+/* ------------------ End of namednodemap functions ----------------------- */
+
+/**
+ * Clone all the element's attributes
+ *
+ * \param sh The source attributes hash table
+ * \param sd The source document
+ * \param th The target hash table
+ * \param td The target document
+ * \param parent The parent of the cloned Node
+ */
+dom_exception attr_clone(struct hash_table *sh, struct dom_document *sd,
+ struct hash_table **th, struct dom_document *td, dom_node_internal *parent)
+{
+ lwc_string *key;
+ lwc_context *sc, *tc;
+ dom_attr *attr;
+ unsigned int c1, *c2 = NULL;
+ dom_exception err;
+
+ err = dom_document_create_hashtable(td, hash_get_chains(sh),
+ hash_get_func(sh), th);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ sc = dom_document_get_intern_context(sd);
+ tc = dom_document_get_intern_context(td);
+ assert(sc != NULL);
+ assert(tc != NULL);
+
+ while ( (key = (lwc_string *) hash_iterate(sh, &c1, &c2)) != NULL) {
+ attr = (dom_attr *) hash_get(sh, key);
+ if (attr != NULL) {
+ lwc_string *nk;
+ lwc_error lerr;
+ dom_attr *na;
+
+ /* we should copy this entry */
+ if (sc != tc) {
+ lerr = lwc_context_intern(tc, lwc_string_data(key),
+ lwc_string_length(key), &nk);
+ if (lerr != lwc_error_ok) {
+ hash_destroy(*th, _key, tc, _value, tc);
+ return dom_exception_from_lwc_error(lerr);
+ }
+ } else {
+ nk = key;
+ lwc_context_string_ref(sc, nk);
+ }
+
+ err = dom_document_import_node(td, attr, true, (void *) &na);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ dom_node_set_parent(na, parent);
+
+ if (hash_add(*th, nk, na, false) == false) {
+ hash_destroy(*th, _key, tc, _value, tc);
+ lwc_context_string_unref(tc, nk);
+ dom_node_unref(na);
+ return DOM_NO_MEM_ERR;
+ }
+ }
+ }
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * The key_func of the hash table, see utils/hashtable.h for details
+ */
+void *_key(void *key, void *key_pw, dom_alloc alloc, void *pw,
+ bool clone)
+{
+ assert(key != NULL);
+ assert(key_pw != NULL);
+
+ UNUSED(alloc);
+ UNUSED(pw);
+
+ if (clone == false) {
+ lwc_context_string_unref((lwc_context *) key_pw, (lwc_string *) key);
+ return NULL;
+ } else {
+ /* TODO: If we want to change tha attr_clone function, we should
+ * fill this part
+ */
+ return NULL;
+ }
+}
+
+/**
+ * The value_func of the hash table, see utils/hashtable.h for details
+ */
+void *_value(void *value, void *value_pw, dom_alloc alloc,
+ void *pw, bool clone)
+{
+ assert(value != NULL);
+ assert(value_pw != NULL);
+
+ UNUSED(value_pw); //jmb
+ UNUSED(alloc);
+ UNUSED(pw);
+
+ if (clone == false) {
+ dom_node_internal *a = (dom_node_internal *) value;
+ a->parent = NULL;
+ dom_node_try_destroy(a);
+ return NULL;
+ } else {
+ /* TODO: If we want to change tha attr_clone function, we should
+ * fill this part
+ */
+ return NULL;
+ }
+}
+
+/**
+ * The value_func of the hash table, see utils/hashtable.h for details
+ */
+void *_nsattributes(void *value, void *value_pw, dom_alloc alloc,
+ void *pw, bool clone)
+{
+ assert(value != NULL);
+ assert(value_pw != NULL);
+
+ UNUSED(alloc);
+ UNUSED(pw);
+
+ if (clone == false) {
+ hash_destroy((struct hash_table *) value, _key, value_pw,
+ _value, value_pw);
+ return NULL;
+ } else {
+ /* TODO: If we want to change tha attr_clone function, we should
+ * fill this part
+ */
+ return NULL;
+ }
+}
Index: src/core/characterdata.h
===================================================================
--- src/core/characterdata.h (revision 8909)
+++ src/core/characterdata.h (working copy)
@@ -19,15 +19,17 @@
struct dom_node_internal base; /**< Base node */
};
+/* The CharacterData is a intermediate node type, so the following function
+ * may never be used */
dom_characterdata *dom_characterdata_create(struct dom_document *doc);
dom_exception dom_characterdata_initialise(struct dom_characterdata *cdata,
struct dom_document *doc, dom_node_type type,
- struct dom_string *name, struct dom_string *value);
+ struct lwc_string_s *name, struct dom_string *value);
void dom_characterdata_finalise(struct dom_document *doc,
struct dom_characterdata *cdata);
-/* The virtual functions for characterdata */
+/* The virtual functions for dom_characterdata */
dom_exception _dom_characterdata_get_data(struct dom_characterdata *cdata,
struct dom_string **data);
dom_exception _dom_characterdata_set_data(struct dom_characterdata *cdata,
@@ -57,4 +59,21 @@
_dom_characterdata_delete_data, \
_dom_characterdata_replace_data
+/* Follwoing comes the protected vtable
+ *
+ * Only the _copy function can be used by sub-class of this.
+ */
+void _dom_characterdata_destroy(struct dom_node_internal *node);
+dom_exception _dom_characterdata_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret);
+dom_exception _dom_characterdata_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old);
+
+#define DOM_CHARACTERDATA_PROTECT_VTABLE \
+ _dom_characterdata_destroy, \
+ _dom_characterdata_alloc, \
+ _dom_characterdata_copy
+
+extern struct dom_characterdata_vtable characterdata_vtable;
+
#endif
Index: src/core/Makefile
===================================================================
--- src/core/Makefile (revision 8909)
+++ src/core/Makefile (working copy)
@@ -1,8 +1,11 @@
# Sources
-DIR_SOURCES := attr.c cdatasection.c characterdata.c comment.c \
- document.c document_type.c doc_fragment.c \
- element.c entity_ref.c implementation.c impllist.c \
- namednodemap.c node.c nodelist.c \
- pi.c string.c text.c
+DIR_SOURCES := \
+ string.c node.c \
+ attr.c characterdata.c element.c \
+ implementation.c impllist.c \
+ text.c typeinfo.c comment.c \
+ namednodemap.c nodelist.c \
+ cdatasection.c document_type.c entity_ref.c pi.c \
+ doc_fragment.c document.c
include build/makefiles/Makefile.subdir
Index: src/core/doc_fragment.h
===================================================================
--- src/core/doc_fragment.h (revision 8909)
+++ src/core/doc_fragment.h (working copy)
@@ -13,12 +13,30 @@
struct dom_document_fragment;
struct dom_document;
struct dom_string;
+struct lwc_string_s;
dom_exception dom_document_fragment_create(struct dom_document *doc,
- struct dom_string *name, struct dom_string *value,
+ struct lwc_string_s *name, struct dom_string *value,
struct dom_document_fragment **result);
void dom_document_fragment_destroy(struct dom_document *doc,
struct dom_document_fragment *frag);
+#define dom_document_fragment_initialise dom_node_initialise
+#define dom_document_fragment_finalise dom_node_finalise
+
+
+/* Following comes the protected vtable */
+void _dom_df_destroy(struct dom_node_internal *node);
+dom_exception _dom_df_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret);
+dom_exception _dom_df_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old);
+
+#define DOM_DF_PROTECT_VTABLE \
+ _dom_df_destroy, \
+ _dom_df_alloc, \
+ _dom_df_copy
+
+
#endif
Index: src/core/element.h
===================================================================
--- src/core/element.h (revision 8909)
+++ src/core/element.h (working copy)
@@ -19,6 +19,7 @@
struct dom_string;
struct dom_attr;
struct dom_type_info;
+struct hash_table;
/**
* DOM element node
@@ -26,33 +27,30 @@
struct dom_element {
struct dom_node_internal base; /**< Base node */
- struct dom_attr *attributes; /**< Element attributes */
+ struct hash_table *attributes; /**< Element attributes */
+ struct hash_table *ns_attributes; /**< Attributes with prefix */
+
+ struct lwc_string_s *id_ns; /**< The id attribute's namespace */
+
+ struct lwc_string_s *id_name; /**< the id attribute's name */
+
struct dom_type_info *schema_type_info; /**< Type information */
};
dom_exception dom_element_create(struct dom_document *doc,
- struct dom_string *name, struct dom_string *namespace,
- struct dom_string *prefix, struct dom_element **result);
+ struct lwc_string_s *name, struct lwc_string_s *namespace,
+ struct lwc_string_s *prefix, struct dom_element **result);
-dom_exception dom_element_initialise(struct dom_element *el,
- struct dom_string *name, struct dom_string *namespace,
- struct dom_string *prefix, struct dom_element **result);
+dom_exception dom_element_initialise(struct dom_element *el,
+ struct dom_document *doc, struct lwc_string_s *name,
+ struct lwc_string_s *namespace, struct lwc_string_s *prefix,
+ struct dom_element **result);
void dom_element_destroy(struct dom_document *doc,
struct dom_element *element);
-void _dom_element_destroy(struct dom_node_internal *node);
-dom_exception dom_element_get_attributes(struct dom_element *element,
- struct dom_namednodemap **result);
-
-dom_exception dom_element_has_attributes(struct dom_element *element,
- bool *result);
-
-struct dom_node_internal *dom_element_get_first_attribute(
- struct dom_element *element);
-
/* The virtual functions of dom_element */
dom_exception _dom_element_get_tag_name(struct dom_element *element,
struct dom_string **name);
@@ -123,4 +121,70 @@
_dom_element_set_id_attribute_ns, \
_dom_element_set_id_attribute_node
+/* Overloading dom_node functions */
+dom_exception _dom_element_get_attributes(dom_node_internal *node,
+ struct dom_namednodemap **result);
+dom_exception _dom_element_has_attributes(dom_node_internal *node, bool *result);
+dom_exception _dom_element_normalize(dom_node_internal *node);
+dom_exception _dom_element_lookup_prefix(dom_node_internal *node,
+ struct dom_string *namespace, struct dom_string **result);
+dom_exception _dom_element_is_default_namespace(dom_node_internal *node,
+ struct dom_string *namespace, bool *result);
+dom_exception _dom_element_lookup_namespace(dom_node_internal *node,
+ struct dom_string *prefix, struct dom_string **result);
+#define DOM_NODE_VTABLE_ELEMENT \
+ _dom_node_get_node_name, \
+ _dom_node_get_node_value, \
+ _dom_node_set_node_value, \
+ _dom_node_get_node_type, \
+ _dom_node_get_parent_node, \
+ _dom_node_get_child_nodes, \
+ _dom_node_get_first_child, \
+ _dom_node_get_last_child, \
+ _dom_node_get_previous_sibling, \
+ _dom_node_get_next_sibling, \
+ _dom_element_get_attributes, /*overload*/\
+ _dom_node_get_owner_document, \
+ _dom_node_insert_before, \
+ _dom_node_replace_child, \
+ _dom_node_remove_child, \
+ _dom_node_append_child, \
+ _dom_node_has_child_nodes, \
+ _dom_node_clone_node, \
+ _dom_node_normalize, \
+ _dom_node_is_supported, \
+ _dom_node_get_namespace, \
+ _dom_node_get_prefix, \
+ _dom_node_set_prefix, \
+ _dom_node_get_local_name, \
+ _dom_element_has_attributes, /*overload*/\
+ _dom_node_get_base, \
+ _dom_node_compare_document_position, \
+ _dom_node_get_text_content, \
+ _dom_node_set_text_content, \
+ _dom_node_is_same, \
+ _dom_element_lookup_prefix, /*overload*/\
+ _dom_element_is_default_namespace, /*overload*/\
+ _dom_element_lookup_namespace, /*overload*/\
+ _dom_node_is_equal, \
+ _dom_node_get_feature, \
+ _dom_node_set_user_data, \
+ _dom_node_get_user_data
+
+
+/* The protected virtual function */
+void _dom_element_destroy(dom_node_internal *node);
+dom_exception _dom_element_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret);
+dom_exception _dom_element_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old);
+
+#define DOM_ELEMENT_PROTECT_VTABLE \
+ _dom_element_destroy, \
+ _dom_element_alloc, \
+ _dom_element_copy
+
+/* Helper functions*/
+dom_exception dom_element_get_id(struct dom_element *ele, struct lwc_string_s **id);
+
#endif
Index: src/core/cdatasection.c
===================================================================
--- src/core/cdatasection.c (revision 8909)
+++ src/core/cdatasection.c (working copy)
@@ -3,11 +3,13 @@
* Licensed under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
* Copyright 2007 John-Mark Bell <jmb(a)netsurf-browser.org>
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
*/
#include "core/cdatasection.h"
#include "core/document.h"
#include "core/text.h"
+#include "utils/utils.h"
/**
* A DOM CDATA section
@@ -16,6 +18,10 @@
struct dom_text base; /**< Base node */
};
+static struct dom_node_protect_vtable cdata_section_protect_vtable = {
+ DOM_CDATA_SECTION_PROTECT_VTABLE
+};
+
/**
* Create a CDATA section
*
@@ -31,7 +37,7 @@
* The returned node will already be referenced.
*/
dom_exception dom_cdata_section_create(struct dom_document *doc,
- struct dom_string *name, struct dom_string *value,
+ struct lwc_string_s *name, struct dom_string *value,
struct dom_cdata_section **result)
{
struct dom_cdata_section *c;
@@ -41,9 +47,13 @@
c = dom_document_alloc(doc, NULL, sizeof(struct dom_cdata_section));
if (c == NULL)
return DOM_NO_MEM_ERR;
+
+ /* Set up vtable */
+ ((dom_node_internal *) c)->base.vtable = &text_vtable;
+ ((dom_node_internal *) c)->vtable = &cdata_section_protect_vtable;
/* And initialise the node */
- err = dom_text_initialise(&c->base, doc, DOM_CDATA_SECTION_NODE,
+ err = dom_cdata_section_initialise(&c->base, doc, DOM_CDATA_SECTION_NODE,
name, value);
if (err != DOM_NO_ERR) {
dom_document_alloc(doc, c, 0);
@@ -67,8 +77,45 @@
struct dom_cdata_section *cdata)
{
/* Clean up base node contents */
- dom_text_finalise(doc, &cdata->base);
+ dom_cdata_section_finalise(doc, &cdata->base);
/* Destroy the node */
dom_document_alloc(doc, cdata, 0);
}
+
+
+/* The protected virtual functions */
+void _dom_cdata_section_destroy(struct dom_node_internal *node)
+{
+ struct dom_document *doc;
+ doc = dom_node_get_owner(node);
+
+ dom_cdata_section_destroy(doc, (struct dom_cdata_section *) node);
+}
+
+/* */
+/*--------------------------------------------------------------------------*/
+/* */
+/* The protected virtual functions, see core/node.h for details */
+dom_exception _dom_cdata_section_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret)
+{
+ UNUSED(n);
+ dom_cdata_section *a;
+
+ a = dom_document_alloc(doc, NULL, sizeof(struct dom_cdata_section));
+ if (a == NULL)
+ return DOM_NO_MEM_ERR;
+
+ *ret = (dom_node_internal *) a;
+ dom_node_set_owner(*ret, doc);
+
+ return DOM_NO_ERR;
+}
+
+dom_exception _dom_cdata_section_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old)
+{
+ return _dom_characterdata_copy(new, old);
+}
+
Index: src/core/cdatasection.h
===================================================================
--- src/core/cdatasection.h (revision 8909)
+++ src/core/cdatasection.h (working copy)
@@ -9,16 +9,35 @@
#define dom_internal_core_cdatasection_h_
#include <dom/core/exceptions.h>
+#include <dom/core/cdatasection.h>
+struct dom_node_internal;
struct dom_cdata_section;
struct dom_document;
struct dom_string;
+struct lwc_string_s;
dom_exception dom_cdata_section_create(struct dom_document *doc,
- struct dom_string *name, struct dom_string *value,
+ struct lwc_string_s *name, struct dom_string *value,
struct dom_cdata_section **result);
void dom_cdata_section_destroy(struct dom_document *doc,
struct dom_cdata_section *cdata);
+#define dom_cdata_section_initialise dom_text_initialise
+#define dom_cdata_section_finalise dom_text_finalise
+
+/* Following comes the protected vtable */
+void _dom_cdata_section_destroy(struct dom_node_internal *node);
+dom_exception _dom_cdata_section_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret);
+dom_exception _dom_cdata_section_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old);
+
+#define DOM_CDATA_SECTION_PROTECT_VTABLE \
+ _dom_cdata_section_destroy, \
+ _dom_cdata_section_alloc, \
+ _dom_cdata_section_copy
+
+
#endif
Index: src/core/entity_ref.c
===================================================================
--- src/core/entity_ref.c (revision 8909)
+++ src/core/entity_ref.c (working copy)
@@ -3,8 +3,11 @@
* Licensed under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
* Copyright 2007 John-Mark Bell <jmb(a)netsurf-browser.org>
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
*/
+#include <libwapcaplet/libwapcaplet.h>
+
#include "core/document.h"
#include "core/entity_ref.h"
#include "core/node.h"
@@ -17,6 +20,14 @@
struct dom_node_internal base; /**< Base node */
};
+static struct dom_node_vtable ef_vtable = {
+ DOM_NODE_VTABLE
+};
+
+static struct dom_node_protect_vtable ef_protect_vtable = {
+ DOM_EF_PROTECT_VTABLE
+};
+
/**
* Create an entity reference
*
@@ -32,7 +43,7 @@
* The returned node will already be referenced.
*/
dom_exception dom_entity_reference_create(struct dom_document *doc,
- struct dom_string *name, struct dom_string *value,
+ struct lwc_string_s *name, struct dom_string *value,
struct dom_entity_reference **result)
{
struct dom_entity_reference *e;
@@ -44,9 +55,12 @@
if (e == NULL)
return DOM_NO_MEM_ERR;
+ e->base.base.vtable = &ef_vtable;
+ e->base.vtable = &ef_protect_vtable;
+
/* And initialise the node */
- err = dom_node_initialise(&e->base, doc, DOM_ENTITY_REFERENCE_NODE,
- name, value, NULL, NULL);
+ err = dom_entity_reference_initialise(&e->base, doc,
+ DOM_ENTITY_REFERENCE_NODE, name, value, NULL, NULL);
if (err != DOM_NO_ERR) {
dom_document_alloc(doc, e, 0);
return err;
@@ -68,32 +82,8 @@
void dom_entity_reference_destroy(struct dom_document *doc,
struct dom_entity_reference *entity)
{
- struct dom_node_internal *c, *d;
-
- /* Destroy children of this node */
- for (c = entity->base.first_child; c != NULL; c = d) {
- d = c->next;
-
- /* Detach child */
- c->parent = NULL;
-
- if (c->refcnt > 0) {
- /* Something is using this child */
-
- /** \todo add to list of nodes pending deletion */
-
- continue;
- }
-
- /* Detach from sibling list */
- c->previous = NULL;
- c->next = NULL;
-
- dom_node_destroy(c);
- }
-
/* Finalise base class */
- dom_node_finalise(doc, &entity->base);
+ dom_entity_reference_finalise(doc, &entity->base);
/* Destroy fragment */
dom_document_alloc(doc, entity, 0);
@@ -119,3 +109,34 @@
return DOM_NOT_SUPPORTED_ERR;
}
+
+/* Following comes the protected vtable */
+void _dom_ef_destroy(struct dom_node_internal *node)
+{
+ dom_entity_reference_destroy(node->owner,
+ (struct dom_entity_reference *) node);
+}
+
+dom_exception _dom_ef_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret)
+{
+ UNUSED(n);
+ dom_entity_reference *a;
+
+ a = dom_document_alloc(doc, NULL, sizeof(struct dom_entity_reference));
+ if (a == NULL)
+ return DOM_NO_MEM_ERR;
+
+ *ret = (dom_node_internal *) a;
+ dom_node_set_owner(*ret, doc);
+
+ return DOM_NO_ERR;
+
+}
+
+dom_exception _dom_ef_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old)
+{
+ return _dom_node_copy(new, old);
+}
+
Index: src/core/pi.c
===================================================================
--- src/core/pi.c (revision 8909)
+++ src/core/pi.c (working copy)
@@ -9,6 +9,8 @@
#include "core/node.h"
#include "core/pi.h"
+#include "utils/utils.h"
+
/**
* A DOM processing instruction
*/
@@ -16,6 +18,13 @@
struct dom_node_internal base; /**< Base node */
};
+static struct dom_node_vtable pi_vtable = {
+ DOM_NODE_VTABLE
+};
+
+static struct dom_node_protect_vtable pi_protect_vtable = {
+ DOM_PI_PROTECT_VTABLE
+};
/**
* Create a processing instruction
*
@@ -31,7 +40,7 @@
* The returned node will already be referenced.
*/
dom_exception dom_processing_instruction_create(struct dom_document *doc,
- struct dom_string *name, struct dom_string *value,
+ struct lwc_string_s *name, struct dom_string *value,
struct dom_processing_instruction **result)
{
struct dom_processing_instruction *p;
@@ -42,9 +51,12 @@
sizeof(struct dom_processing_instruction));
if (p == NULL)
return DOM_NO_MEM_ERR;
+
+ p->base.base.vtable = &pi_vtable;
+ p->base.vtable = &pi_protect_vtable;
/* And initialise the node */
- err = dom_node_initialise(&p->base, doc,
+ err = dom_processing_instruction_initialise(&p->base, doc,
DOM_PROCESSING_INSTRUCTION_NODE,
name, value, NULL, NULL);
if (err != DOM_NO_ERR) {
@@ -69,8 +81,39 @@
struct dom_processing_instruction *pi)
{
/* Finalise base class */
- dom_node_finalise(doc, &pi->base);
+ dom_processing_instruction_finalise(doc, &pi->base);
/* Free processing instruction */
dom_document_alloc(doc, pi, 0);
}
+
+
+/* Following comes the protected vtable */
+void _dom_pi_destroy(struct dom_node_internal *node)
+{
+ dom_processing_instruction_destroy(node->owner,
+ (struct dom_processing_instruction *) node);
+}
+
+
+dom_exception _dom_pi_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret)
+{
+ UNUSED(n);
+ struct dom_processing_instruction *a;
+
+ a = dom_document_alloc(doc, NULL, sizeof(struct dom_processing_instruction));
+ if (a == NULL)
+ return DOM_NO_MEM_ERR;
+
+ *ret = (dom_node_internal *) a;
+ dom_node_set_owner(*ret, doc);
+
+ return DOM_NO_ERR;
+}
+
+dom_exception _dom_pi_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old)
+{
+ return _dom_node_copy(new, old);
+}
Index: src/core/attr.c
===================================================================
--- src/core/attr.c (revision 8909)
+++ src/core/attr.c (working copy)
@@ -3,10 +3,12 @@
* Licensed under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
* Copyright 2007 John-Mark Bell <jmb(a)netsurf-browser.org>
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
*/
#include <stddef.h>
#include <string.h>
+#include <assert.h>
#include <dom/core/attr.h>
#include <dom/core/document.h>
@@ -27,22 +29,32 @@
struct dom_attr {
struct dom_node_internal base; /**< Base node */
- bool specified; /**< Whether attribute was specified
- * or defaulted */
+ bool specified; /**< Whether the attribute is specified
+ * or default */
struct dom_type_info *schema_type_info; /**< Type information */
- bool is_id; /**< Attribute is of type ID */
+ bool is_id; /**< Whether this attribute is a ID attribute */
};
-/* The vtable for dom attr node */
+/* The vtable for dom_attr node */
static struct dom_attr_vtable attr_vtable = {
{
- DOM_NODE_VTABLE
+ DOM_NODE_VTABLE_ATTR
},
DOM_ATTR_VTABLE
};
+/* The protected vtable for dom_attr */
+static struct dom_node_protect_vtable attr_protect_vtable = {
+ DOM_ATTR_PROTECT_VTABLE
+};
+
+/* */
+/* -------------------------------------------------------------------- */
+/* */
+/* Constructor and destructor */
+
/**
* Create an attribute node
*
@@ -50,6 +62,7 @@
* \param name The (local) name of the node to create
* \param namespace The namespace URI of the attribute, or NULL
* \param prefix The namespace prefix of the attribute, or NULL
+ * \param specified Whtether this attribute is specified
* \param result Pointer to location to receive created attribute
* \return DOM_NO_ERR on success,
* DOM_INVALID_CHARACTER_ERR if ::name is invalid,
@@ -60,8 +73,9 @@
* The returned attribute will already be referenced.
*/
dom_exception dom_attr_create(struct dom_document *doc,
- struct dom_string *name, struct dom_string *namespace,
- struct dom_string *prefix, struct dom_attr **result)
+ struct lwc_string_s *name, struct lwc_string_s *namespace,
+ struct lwc_string_s *prefix, bool specified,
+ struct dom_attr **result)
{
struct dom_attr *a;
dom_exception err;
@@ -75,18 +89,44 @@
/* Initialise the vtable */
a->base.base.vtable = &attr_vtable;
- a->base.destroy = _dom_attr_destroy;
+ a->base.vtable = &attr_protect_vtable;
- /* Initialise the base class */
+ /* Initialise the class */
+ err = dom_attr_initialise(a, doc, name, namespace, prefix, specified,
+ result);
+ if (err != DOM_NO_ERR) {
+ dom_document_alloc(doc, a, 0);
+ return err;
+ }
+ return DOM_NO_ERR;
+}
+
+/**
+ * Initialis a dom_attr
+ *
+ * \param a The dom_attr
+ * \param doc The document
+ * \param name The name of this attribute node
+ * \param namespace The namespace of this attribute
+ * \param prefix The prefix
+ * \param specified Whether this node is a specified one
+ * \param result The returned node
+ */
+dom_exception dom_attr_initialise(dom_attr *a,
+ struct dom_document *doc, struct lwc_string_s *name,
+ struct lwc_string_s *namespace, struct lwc_string_s *prefix,
+ bool specified, struct dom_attr **result)
+{
+ dom_exception err;
+
err = dom_node_initialise(&a->base, doc, DOM_ATTRIBUTE_NODE,
name, NULL, namespace, prefix);
if (err != DOM_NO_ERR) {
- dom_document_alloc(doc, a, 0);
return err;
}
/* Perform our type-specific initialisation */
- a->specified = false;
+ a->specified = specified;
a->schema_type_info = NULL;
a->is_id = false;
@@ -96,39 +136,13 @@
}
/**
- * Destroy an attribute node
+ * The destructor of dom_attr
*
- * \param doc The owning document
- * \param attr The attribute to destroy
- *
- * The contents of ::attr will be destroyed and ::attr will be freed
+ * \param doc The owner document
+ * \param attr The attribute
*/
-void dom_attr_destroy(struct dom_document *doc, struct dom_attr *attr)
+void dom_attr_finalise(dom_document *doc, dom_attr *attr)
{
- struct dom_node_internal *c, *d;
-
- /* Destroy children of this node */
- for (c = attr->base.first_child; c != NULL; c = d) {
- d = c->next;
-
- /* Detach child */
- c->parent = NULL;
-
- if (c->refcnt > 0) {
- /* Something is using this child */
-
- /** \todo add to list of nodes pending deletion */
-
- continue;
- }
-
- /* Detach from sibling list */
- c->previous = NULL;
- c->next = NULL;
-
- dom_node_destroy(c);
- }
-
/* Now, clean up this node and destroy it */
if (attr->schema_type_info != NULL) {
@@ -136,15 +150,28 @@
}
dom_node_finalise(doc, &attr->base);
-
- dom_document_alloc(doc, attr, 0);
}
-void _dom_attr_destroy(dom_node_internal *node)
+/**
+ * Destroy an attribute node
+ *
+ * \param doc The owning document
+ * \param attr The attribute to destroy
+ *
+ * The contents of ::attr will be destroyed and ::attr will be freed
+ */
+void dom_attr_destroy(struct dom_document *doc, struct dom_attr *attr)
{
- UNUSED(node);
+ dom_attr_finalise(doc, attr);
+
+ dom_document_alloc(doc, attr, 0);
}
+/* */
+/* -------------------------------------------------------------------- */
+/* */
+/* The public virtual functions */
+
/**
* Retrieve an attribute's name
*
@@ -164,7 +191,7 @@
}
/**
- * Determine if attribute was specified or defaulted
+ * Determine if attribute was specified or default
*
* \param attr Attribute to inspect
* \param result Pointer to location to receive result
@@ -197,7 +224,7 @@
dom_exception err;
err = dom_document_create_string(a->owner,
- (const uint8_t *) "", SLEN(""), &value);
+ NULL, 0, &value);
if (err != DOM_NO_ERR) {
return err;
}
@@ -285,25 +312,22 @@
/* Detach child */
c->parent = NULL;
- if (c->refcnt > 0) {
- /* Something is using this child */
-
- /** \todo add to list of nodes pending deletion */
-
- continue;
- }
-
/* Detach from sibling list */
c->previous = NULL;
c->next = NULL;
- dom_node_destroy(c);
+ dom_node_try_destroy(c);
}
/* And insert the text node as the value */
((struct dom_node_internal *) text)->parent = a;
a->first_child = a->last_child = (struct dom_node_internal *) text;
+ dom_node_unref(text);
+ dom_node_remove_pending(text);
+ /* Now the attribute node is specified */
+ attr->specified = true;
+
return DOM_NO_ERR;
}
@@ -363,3 +387,163 @@
return DOM_NO_ERR;
}
+
+/*------------- The overload virtual functions ------------------------*/
+dom_exception _dom_attr_get_node_value(dom_node_internal *node,
+ struct dom_string **result)
+{
+ dom_attr *attr = (dom_attr *) node;
+
+ return _dom_attr_get_value(attr, result);
+}
+
+
+dom_exception _dom_attr_clone_node(dom_node_internal *node, bool deep,
+ dom_node_internal **result)
+{
+ dom_exception err;
+ dom_attr *attr;
+
+ /* Discard the warnings */
+ UNUSED(deep);
+
+ /* Clone an Attr always clone all its children */
+ err = _dom_node_clone_node(node, true, result);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ attr = (dom_attr *) *result;
+ /* Clone an Attr always result a specified Attr,
+ * see DOM Level 3 Node.cloneNode */
+ attr->specified = true;
+
+ return DOM_NO_ERR;
+}
+
+
+dom_exception _dom_attr_set_prefix(dom_node_internal *node,
+ struct dom_string *prefix)
+{
+ /* Really I don't know whether there should something
+ * special to do here */
+ return _dom_node_set_prefix(node, prefix);
+}
+
+dom_exception _dom_attr_lookup_prefix(dom_node_internal *node,
+ struct dom_string *namespace, struct dom_string **result)
+{
+ struct dom_element *owner;
+ dom_exception err;
+
+ err = dom_attr_get_owner_element(node, &owner);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ if (owner == NULL) {
+ *result = NULL;
+ return DOM_NO_ERR;
+ }
+
+ return dom_node_lookup_prefix(owner, namespace, result);
+}
+
+dom_exception _dom_attr_is_default_namespace(dom_node_internal *node,
+ struct dom_string *namespace, bool *result)
+{
+ struct dom_element *owner;
+ dom_exception err;
+
+ err = dom_attr_get_owner_element(node, &owner);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ if (owner == NULL) {
+ *result = false;
+ return DOM_NO_ERR;
+ }
+
+ return dom_node_is_default_namespace(owner, namespace, result);
+}
+
+dom_exception _dom_attr_lookup_namespace(dom_node_internal *node,
+ struct dom_string *prefix, struct dom_string **result)
+{
+ struct dom_element *owner;
+ dom_exception err;
+
+ err = dom_attr_get_owner_element(node, &owner);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ if (owner == NULL) {
+ *result = NULL;
+ return DOM_NO_ERR;
+ }
+
+ return dom_node_lookup_namespace(owner, prefix, result);
+}
+
+/* */
+/*----------------------------------------------------------------------*/
+/* */
+/* The protected virtual functions */
+void _dom_attr_destroy(dom_node_internal *node)
+{
+ dom_document *doc = node->owner;
+
+ assert(doc != NULL);
+ dom_attr_destroy(doc, (dom_attr *) node);
+}
+
+dom_exception _dom_attr_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret)
+{
+ UNUSED(n);
+ dom_attr *a;
+
+ a = dom_document_alloc(doc, NULL, sizeof(struct dom_attr));
+ if (a == NULL)
+ return DOM_NO_MEM_ERR;
+
+ *ret = (dom_node_internal *) a;
+ dom_node_set_owner(*ret, doc);
+
+ return DOM_NO_ERR;
+}
+
+dom_exception _dom_attr_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old)
+{
+ dom_attr *na = (dom_attr *) new;
+ dom_attr *oa = (dom_attr *) old;
+
+ na->specified = oa->specified;
+ na->is_id = oa->is_id;
+
+ /* TODO: deal with dom_type_info, it get no definition ! */
+
+ return _dom_node_copy(new, old);
+}
+
+
+/**
+ * Set/Unset whether this attribute is a ID attribute
+ *
+ * \param attr The attribute
+ * \param is_id Whether it is a ID attribute
+ */
+void dom_attr_set_isid(struct dom_attr *attr, bool is_id)
+{
+ attr->is_id = is_id;
+}
+
+/**
+ * Set/Unset whether the attribute is a specified one.
+ *
+ * \param attr The attribute node
+ * \param specified Whether this attribute is a specified one
+ */
+void dom_attr_set_specified(struct dom_attr *attr, bool specified)
+{
+ attr->specified = specified;
+}
Index: src/core/entity_ref.h
===================================================================
--- src/core/entity_ref.h (revision 8909)
+++ src/core/entity_ref.h (working copy)
@@ -9,19 +9,38 @@
#define dom_internal_core_entityreference_h_
#include <dom/core/exceptions.h>
+#include <dom/core/entity_ref.h>
struct dom_document;
struct dom_entity_reference;
struct dom_string;
+struct lwc_string_s;
dom_exception dom_entity_reference_create(struct dom_document *doc,
- struct dom_string *name, struct dom_string *value,
+ struct lwc_string_s *name, struct dom_string *value,
struct dom_entity_reference **result);
void dom_entity_reference_destroy(struct dom_document *doc,
struct dom_entity_reference *entity);
+#define dom_entity_reference_initialise dom_node_initialise
+#define dom_entity_reference_finalise dom_node_finalise
+
+/* Following comes the protected vtable */
+void _dom_ef_destroy(struct dom_node_internal *node);
+dom_exception _dom_ef_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret);
+dom_exception _dom_ef_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old);
+
+#define DOM_EF_PROTECT_VTABLE \
+ _dom_ef_destroy, \
+ _dom_ef_alloc, \
+ _dom_ef_copy
+
+/* Helper functions */
dom_exception dom_entity_reference_get_textual_representation(
struct dom_entity_reference *entity,
struct dom_string **result);
+
#endif
Index: src/core/pi.h
===================================================================
--- src/core/pi.h (revision 8909)
+++ src/core/pi.h (working copy)
@@ -13,12 +13,28 @@
struct dom_document;
struct dom_processing_instruction;
struct dom_string;
+struct lwc_string_s;
dom_exception dom_processing_instruction_create(struct dom_document *doc,
- struct dom_string *name, struct dom_string *value,
+ struct lwc_string_s *name, struct dom_string *value,
struct dom_processing_instruction **result);
void dom_processing_instruction_destroy(struct dom_document *doc,
struct dom_processing_instruction *pi);
+#define dom_processing_instruction_initialise dom_node_initialise
+#define dom_processing_instruction_finalise dom_node_finalise
+
+/* Following comes the protected vtable */
+void _dom_pi_destroy(struct dom_node_internal *node);
+dom_exception _dom_pi_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret);
+dom_exception _dom_pi_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old);
+
+#define DOM_PI_PROTECT_VTABLE \
+ _dom_pi_destroy, \
+ _dom_pi_alloc, \
+ _dom_pi_copy
+
#endif
Index: src/core/document.c
===================================================================
--- src/core/document.c (revision 8909)
+++ src/core/document.c (working copy)
@@ -3,16 +3,23 @@
* Licensed under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
* Copyright 2007 John-Mark Bell <jmb(a)netsurf-browser.org>
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
*/
+#include <assert.h>
+
#include <string.h>
+#include <libwapcaplet/libwapcaplet.h>
+
#include <dom/functypes.h>
#include <dom/bootstrap/implpriv.h>
+#include <dom/core/attr.h>
+#include <dom/core/element.h>
#include <dom/core/document.h>
#include <dom/core/implementation.h>
-#include <dom/core/string.h>
+#include "core/string.h"
#include "core/attr.h"
#include "core/cdatasection.h"
#include "core/comment.h"
@@ -24,6 +31,7 @@
#include "core/nodelist.h"
#include "core/pi.h"
#include "core/text.h"
+#include "utils/validate.h"
#include "utils/namespace.h"
#include "utils/utils.h"
@@ -37,19 +45,9 @@
struct dom_doc_nl *prev; /**< Previous item */
};
-/**
- * Item in list of active namednodemaps
- */
-struct dom_doc_nnm {
- struct dom_namednodemap *map; /**< Named node map */
- struct dom_doc_nnm *next; /**< Next map */
- struct dom_doc_nnm *prev; /**< Previous map */
-};
-
-
-/** Interned node name strings, indexed by node type */
-/* Index 0 is unused */
+/** Node name strings, indexed by node type
+ * Index 0 is unused */
static struct dom_string *__nodenames_utf8[DOM_NODE_TYPE_COUNT + 1];
/* The virtual functions of this dom_document */
@@ -60,6 +58,23 @@
DOM_DOCUMENT_VTABLE
};
+static struct dom_node_protect_vtable document_protect_vtable = {
+ DOM_DOCUMENT_PROTECT_VTABLE
+};
+
+/* */
+/*----------------------------------------------------------------------*/
+/* */
+/* Intetnally used helper functions */
+static dom_exception dom_document_dup_node(dom_document *doc,
+ struct dom_node *node, bool deep, struct dom_node **result,
+ dom_node_operation opt);
+
+/* */
+/*----------------------------------------------------------------------*/
+/* */
+/* The Document Module level initialise and finalise functions */
+
/**
* Initialise the document module
*
@@ -67,7 +82,7 @@
* \param pw Pointer to client-specific private data
* \return DOM_NO_ERR on success
*/
-dom_exception _dom_document_initialise(dom_alloc alloc, void *pw)
+dom_exception dom_document_module_initialise(dom_alloc alloc, void *pw)
{
static struct {
const char *name;
@@ -120,7 +135,7 @@
*
* \return DOM_NO_ERR.
*/
-dom_exception _dom_document_finalise(void)
+dom_exception dom_document_module_finalise(void)
{
for (int i = 0; i <= DOM_NODE_TYPE_COUNT; i++) {
if (__nodenames_utf8[i] != NULL) {
@@ -131,6 +146,11 @@
return DOM_NO_ERR;
}
+/* */
+/*----------------------------------------------------------------------*/
+/* */
+/* The constructors and destructors */
+
/**
* Create a Document
*
@@ -138,6 +158,7 @@
* \param alloc Memory (de)allocation function
* \param pw Pointer to client-specific private data
* \param doc Pointer to location to receive created document
+ * \param ctx The intern string context of this document
* \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion.
*
* ::impl will have its reference count increased.
@@ -145,7 +166,8 @@
* The returned document will already be referenced.
*/
dom_exception dom_document_create(struct dom_implementation *impl,
- dom_alloc alloc, void *pw, struct dom_document **doc)
+ dom_alloc alloc, void *pw, struct lwc_context_s *ctx,
+ struct dom_document **doc)
{
struct dom_document *d;
dom_exception err;
@@ -155,103 +177,103 @@
if (d == NULL)
return DOM_NO_MEM_ERR;
- /* Set up document allocation context - must be first */
- d->alloc = alloc;
- d->pw = pw;
-
/* Initialise the virtual table */
d->base.base.vtable = &document_vtable;
- d->base.destroy = &dom_document_destroy;
+ d->base.vtable = &document_protect_vtable;
/* Initialise base class -- the Document has no parent, so
* destruction will be attempted as soon as its reference count
* reaches zero. Documents own themselves (this simplifies the
* rest of the code, as it doesn't need to special case Documents)
*/
- err = dom_node_initialise(&d->base, d, DOM_DOCUMENT_NODE,
- NULL, NULL, NULL, NULL);
+ err = dom_document_initialise(d, impl, alloc, pw, ctx);
if (err != DOM_NO_ERR) {
/* Clean up document */
alloc(d, 0, pw);
return err;
}
- /* Initialise remaining type-specific data */
- if (impl != NULL)
- dom_implementation_ref(impl);
- d->impl = impl;
-
- d->nodelists = NULL;
- d->maps = NULL;
-
- d->nodenames = __nodenames_utf8;
-
*doc = d;
return DOM_NO_ERR;
}
-/**
- * Destroy a document
- *
- * \param doc The document to destroy, which is passed in as a
- *
- * dom_node_internl
- *
- * The contents of ::doc will be destroyed and ::doc will be freed.
- */
-void dom_document_destroy(struct dom_node_internal *dnode)
+/* Initialise the document */
+dom_exception dom_document_initialise(struct dom_document *doc,
+ struct dom_implementation *impl, dom_alloc alloc, void *pw,
+ struct lwc_context_s *ctx)
{
- struct dom_document *doc = (struct dom_document *) dnode;
- struct dom_node_internal *c, *d;
+ assert(ctx != NULL);
+ assert(alloc != NULL);
+ assert(impl != NULL);
- /* Destroy children of this node */
- for (c = doc->base.first_child; c != NULL; c = d) {
- d = c->next;
+ dom_exception err;
+ lwc_string *name;
+
+ err = dom_string_intern(__nodenames_utf8[DOM_DOCUMENT_NODE], ctx, &name);
+ if (err != DOM_NO_ERR)
+ return err;
- /* Detach child */
- c->parent = NULL;
+ dom_implementation_ref(impl);
+ doc->impl = impl;
- if (c->refcnt > 0) {
- /* Something is using this child */
+ doc->nodelists = NULL;
- /** \todo add to list of nodes pending deletion */
+ doc->nodenames = __nodenames_utf8;
- continue;
- }
+ /* Set up document allocation context - must be first */
+ doc->alloc = alloc;
+ doc->pw = pw;
+ doc->context = ctx;
- /* Detach from sibling list */
- c->previous = NULL;
- c->next = NULL;
+ err = dom_node_initialise(&doc->base, doc, DOM_DOCUMENT_NODE,
+ name, NULL, NULL, NULL);
+ lwc_context_string_unref(ctx, name);
- dom_node_destroy(c);
- }
+ list_init(&doc->pending_nodes);
- /** \todo Ensure list of nodes pending deletion is empty. If not,
+ return err;
+}
+
+
+/* Finalise the document */
+bool dom_document_finalise(struct dom_document *doc)
+{
+ /* Finalise base class, delete the tree in force */
+ dom_node_finalise(doc, &doc->base);
+
+ /* Now, the first_child and last_child should be null */
+ doc->base.first_child = NULL;
+ doc->base.last_child = NULL;
+
+ /**
+ * Ensure list of nodes pending deletion is empty. If not,
* then we can't yet destroy the document (its destruction will
* have to wait until the pending nodes are destroyed) */
+ if (doc->pending_nodes.next != &doc->pending_nodes)
+ return false;
/* Ok, the document tree is empty, as is the list of nodes pending
* deletion. Therefore, it is safe to destroy the document. */
-
if (doc->impl != NULL)
dom_implementation_unref(doc->impl);
doc->impl = NULL;
- /* This is paranoia -- if there are any remaining nodelists or
- * namednodemaps, then the document's reference count will be
+ /* This is paranoia -- if there are any remaining nodelists,
+ * then the document's reference count will be
* non-zero as these data structures reference the document because
* they are held by the client. */
doc->nodelists = NULL;
- doc->maps = NULL;
- /* Finalise base class */
- dom_node_finalise(doc, &doc->base);
-
- /* Free document */
- doc->alloc(doc, 0, doc->pw);
+ return true;
}
+
+/* */
+/*----------------------------------------------------------------------*/
+/* */
+/* Public virtual functions */
+
/**
* Retrieve the doctype of a document
*
@@ -319,7 +341,7 @@
{
struct dom_node_internal *root;
- /* Find first element node in child list */
+ /* Find the first element node in child list */
for (root = doc->base.first_child; root != NULL; root = root->next) {
if (root->type == DOM_ELEMENT_NODE)
break;
@@ -351,7 +373,21 @@
dom_exception _dom_document_create_element(struct dom_document *doc,
struct dom_string *tag_name, struct dom_element **result)
{
- return dom_element_create(doc, tag_name, NULL, NULL, result);
+ lwc_string *name;
+ dom_exception err;
+
+ if (_dom_validate_name(tag_name) == false)
+ return DOM_INVALID_CHARACTER_ERR;
+
+ assert(doc->context != NULL);
+ err = dom_string_intern(tag_name, doc->context, &name);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ err = dom_element_create(doc, name, NULL, NULL, result);
+ lwc_context_string_unref(doc->context, name);
+
+ return err;
}
/**
@@ -368,9 +404,19 @@
dom_exception _dom_document_create_document_fragment(struct dom_document *doc,
struct dom_document_fragment **result)
{
- return dom_document_fragment_create(doc,
- doc->nodenames[DOM_DOCUMENT_FRAGMENT_NODE],
- NULL, result);
+ lwc_string *name;
+ dom_exception err;
+
+ assert(doc->context != NULL);
+ err = dom_string_intern(doc->nodenames[DOM_DOCUMENT_FRAGMENT_NODE],
+ doc->context, &name);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ err = dom_document_fragment_create(doc, name, NULL, result);
+ lwc_context_string_unref(doc->context, name);
+
+ return err;
}
/**
@@ -388,8 +434,19 @@
dom_exception _dom_document_create_text_node(struct dom_document *doc,
struct dom_string *data, struct dom_text **result)
{
- return dom_text_create(doc, doc->nodenames[DOM_TEXT_NODE],
- data, result);
+ lwc_string *name;
+ dom_exception err;
+
+ assert(doc->context != NULL);
+ err = dom_string_intern(doc->nodenames[DOM_TEXT_NODE],
+ doc->context, &name);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ err = dom_text_create(doc, name, data, result);
+ lwc_context_string_unref(doc->context, name);
+
+ return err;
}
/**
@@ -407,8 +464,19 @@
dom_exception _dom_document_create_comment(struct dom_document *doc,
struct dom_string *data, struct dom_comment **result)
{
- return dom_comment_create(doc, doc->nodenames[DOM_COMMENT_NODE],
- data, result);
+ lwc_string *name;
+ dom_exception err;
+
+ assert(doc->context != NULL);
+ err = dom_string_intern(doc->nodenames[DOM_COMMENT_NODE],
+ doc->context, &name);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ err = dom_comment_create(doc, name, data, result);
+ lwc_context_string_unref(doc->context, name);
+
+ return err;
}
/**
@@ -427,9 +495,19 @@
dom_exception _dom_document_create_cdata_section(struct dom_document *doc,
struct dom_string *data, struct dom_cdata_section **result)
{
- return dom_cdata_section_create(doc,
- doc->nodenames[DOM_CDATA_SECTION_NODE],
- data, result);
+ lwc_string *name;
+ dom_exception err;
+
+ assert(doc->context != NULL);
+ err = dom_string_intern(doc->nodenames[DOM_CDATA_SECTION_NODE],
+ doc->context, &name);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ err = dom_cdata_section_create(doc, name, data, result);
+ lwc_context_string_unref(doc->context, name);
+
+ return err;
}
/**
@@ -452,7 +530,21 @@
struct dom_string *data,
struct dom_processing_instruction **result)
{
- return dom_processing_instruction_create(doc, target, data, result);
+ lwc_string *name;
+ dom_exception err;
+
+ if (_dom_validate_name(target) == false)
+ return DOM_INVALID_CHARACTER_ERR;
+
+ assert(doc->context != NULL);
+ err = dom_string_intern(target, doc->context, &name);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ err = dom_processing_instruction_create(doc, name, data, result);
+ lwc_context_string_unref(doc->context, name);
+
+ return err;
}
/**
@@ -471,7 +563,20 @@
dom_exception _dom_document_create_attribute(struct dom_document *doc,
struct dom_string *name, struct dom_attr **result)
{
- return dom_attr_create(doc, name, NULL, NULL, result);
+ lwc_string *n;
+ dom_exception err;
+
+ if (_dom_validate_name(name) == false)
+ return DOM_INVALID_CHARACTER_ERR;
+
+ assert(doc->context != NULL);
+ err = dom_string_intern(name, doc->context, &n);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ err = dom_attr_create(doc, n, NULL, NULL, true, result);
+ lwc_context_string_unref(doc->context, n);
+ return err;
}
/**
@@ -492,7 +597,20 @@
struct dom_string *name,
struct dom_entity_reference **result)
{
- return dom_entity_reference_create(doc, name, NULL, result);
+ lwc_string *n;
+ dom_exception err;
+
+ if (_dom_validate_name(name) == false)
+ return DOM_INVALID_CHARACTER_ERR;
+
+ assert(doc->context != NULL);
+ err = dom_string_intern(name, doc->context, &n);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ err = dom_entity_reference_create(doc, n, NULL, result);
+ lwc_context_string_unref(doc->context, n);
+ return err;
}
/**
@@ -510,8 +628,20 @@
dom_exception _dom_document_get_elements_by_tag_name(struct dom_document *doc,
struct dom_string *tagname, struct dom_nodelist **result)
{
- return dom_document_get_nodelist(doc, (struct dom_node_internal *) doc,
- tagname, NULL, NULL, result);
+ lwc_string *name;
+ dom_exception err;
+
+ assert(doc->context != NULL);
+ err = dom_string_intern(tagname, doc->context, &name);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ err = dom_document_get_nodelist(doc, DOM_NODELIST_BY_NAME,
+ (struct dom_node_internal *) doc, name, NULL, NULL,
+ result);
+ lwc_context_string_unref(doc->context, name);
+
+ return err;
}
/**
@@ -532,12 +662,9 @@
dom_exception _dom_document_import_node(struct dom_document *doc,
struct dom_node *node, bool deep, struct dom_node **result)
{
- UNUSED(doc);
- UNUSED(node);
- UNUSED(deep);
- UNUSED(result);
+ /* TODO: The DOM_INVALID_CHARACTER_ERR exception */
- return DOM_NOT_SUPPORTED_ERR;
+ return dom_document_dup_node(doc, node, deep, result, DOM_NODE_IMPORTED);
}
/**
@@ -575,7 +702,8 @@
struct dom_string *prefix, *localname;
dom_exception err;
- /** \todo ensure document supports XML feature */
+ if (_dom_validate_name(qname) == false)
+ return DOM_INVALID_CHARACTER_ERR;
/* Validate qname */
err = _dom_namespace_validate_qname(qname, namespace);
@@ -589,14 +717,58 @@
return err;
}
+ /* Get the interned string from the dom_string */
+ assert(doc->context != NULL);
+ lwc_string *l = NULL, *n = NULL, *p = NULL;
+ if (localname != NULL) {
+ err = dom_string_intern(localname, doc->context, &l);
+ if (err != DOM_NO_ERR) {
+ dom_string_unref(localname);
+ if (prefix != NULL)
+ dom_string_unref(prefix);
+
+ return err;
+ }
+ }
+ if (namespace != NULL) {
+ err = dom_string_intern(namespace, doc->context, &n);
+ if (err != DOM_NO_ERR) {
+ lwc_context_string_unref(doc->context, l);
+ dom_string_unref(localname);
+ if (prefix != NULL)
+ dom_string_unref(prefix);
+
+ return err;
+ }
+ }
+ if (prefix != NULL) {
+ err = dom_string_intern(prefix, doc->context, &p);
+ if (err != DOM_NO_ERR) {
+ lwc_context_string_unref(doc->context, l);
+ lwc_context_string_unref(doc->context, n);
+ dom_string_unref(localname);
+ if (prefix != NULL)
+ dom_string_unref(prefix);
+
+ return err;
+ }
+ }
+
/* Attempt to create element */
- err = dom_element_create(doc, localname, namespace, prefix, result);
+ err = dom_element_create(doc, l, n, p, result);
/* Tidy up */
- dom_string_unref(localname);
+ if (localname != NULL) {
+ dom_string_unref(localname);
+ lwc_context_string_unref(doc->context, l);
+ }
if (prefix != NULL) {
dom_string_unref(prefix);
+ lwc_context_string_unref(doc->context, p);
}
+ if (namespace != NULL) {
+ lwc_context_string_unref(doc->context, n);
+ }
return err;
}
@@ -636,7 +808,8 @@
struct dom_string *prefix, *localname;
dom_exception err;
- /** \todo ensure document supports XML feature */
+ if (_dom_validate_name(qname) == false)
+ return DOM_INVALID_CHARACTER_ERR;
/* Validate qname */
err = _dom_namespace_validate_qname(qname, namespace);
@@ -650,14 +823,57 @@
return err;
}
+ /* Get the interned string from the dom_string */
+ assert(doc->context != NULL);
+ lwc_string *l = NULL, *n = NULL, *p = NULL;
+ if (localname != NULL) {
+ err = dom_string_intern(localname, doc->context, &l);
+ if (err != DOM_NO_ERR) {
+ dom_string_unref(localname);
+ if (prefix != NULL)
+ dom_string_unref(prefix);
+
+ return err;
+ }
+ }
+ if (namespace != NULL) {
+ err = dom_string_intern(namespace, doc->context, &n);
+ if (err != DOM_NO_ERR) {
+ lwc_context_string_unref(doc->context, l);
+ dom_string_unref(localname);
+ if (prefix != NULL)
+ dom_string_unref(prefix);
+
+ return err;
+ }
+ }
+ if (prefix != NULL) {
+ err = dom_string_intern(prefix, doc->context, &p);
+ if (err != DOM_NO_ERR) {
+ lwc_context_string_unref(doc->context, l);
+ lwc_context_string_unref(doc->context, n);
+ dom_string_unref(localname);
+ if (prefix != NULL)
+ dom_string_unref(prefix);
+
+ return err;
+ }
+ }
/* Attempt to create attribute */
- err = dom_attr_create(doc, localname, namespace, prefix, result);
+ err = dom_attr_create(doc, l, n, p, true, result);
/* Tidy up */
- dom_string_unref(localname);
+ if (localname != NULL) {
+ dom_string_unref(localname);
+ lwc_context_string_unref(doc->context, l);
+ }
if (prefix != NULL) {
dom_string_unref(prefix);
+ lwc_context_string_unref(doc->context, p);
}
+ if (namespace != NULL) {
+ lwc_context_string_unref(doc->context, n);
+ }
return err;
}
@@ -679,8 +895,33 @@
struct dom_document *doc, struct dom_string *namespace,
struct dom_string *localname, struct dom_nodelist **result)
{
- return dom_document_get_nodelist(doc, (struct dom_node_internal *) doc,
- NULL, namespace, localname, result);
+ dom_exception err;
+ lwc_string *l = NULL, *n = NULL;
+
+ /* Get the interned string from the dom_string */
+ assert(doc->context != NULL);
+ if (localname != NULL) {
+ err = dom_string_intern(localname, doc->context, &l);
+ if (err != DOM_NO_ERR)
+ return err;
+ }
+ if (namespace != NULL) {
+ err = dom_string_intern(namespace, doc->context, &n);
+ if (err != DOM_NO_ERR) {
+ lwc_context_string_unref(doc->context, l);
+ return err;
+ }
+ }
+
+ err = dom_document_get_nodelist(doc, DOM_NODELIST_BY_NAMESPACE,
+ (struct dom_node_internal *) doc, NULL, n, l, result);
+
+ if (l != NULL)
+ lwc_context_string_unref(doc->context, l);
+ if (n != NULL)
+ lwc_context_string_unref(doc->context, n);
+
+ return err;
}
/**
@@ -698,11 +939,25 @@
dom_exception _dom_document_get_element_by_id(struct dom_document *doc,
struct dom_string *id, struct dom_element **result)
{
- UNUSED(doc);
- UNUSED(id);
- UNUSED(result);
+ lwc_string *i;
+ dom_node_internal *root;
+ dom_exception err;
- return DOM_NOT_SUPPORTED_ERR;
+ *result = NULL;
+
+ assert(doc->context != NULL);
+ err = dom_string_intern(id, doc->context, &i);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ err = dom_document_get_document_element(doc, (void *) &root);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ err = find_element_by_id(root, i, result);
+ dom_node_unref(root);
+
+ return err;
}
/**
@@ -863,10 +1118,10 @@
dom_exception _dom_document_get_uri(struct dom_document *doc,
struct dom_string **result)
{
- UNUSED(doc);
- UNUSED(result);
+ dom_string_ref(doc->uri);
+ *result = doc->uri;
- return DOM_NOT_SUPPORTED_ERR;
+ return DOM_NO_ERR;
}
/**
@@ -883,10 +1138,11 @@
dom_exception _dom_document_set_uri(struct dom_document *doc,
struct dom_string *uri)
{
- UNUSED(doc);
- UNUSED(uri);
+ dom_string_unref(doc->uri);
+ dom_string_ref(uri);
+ doc->uri = uri;
- return DOM_NOT_SUPPORTED_ERR;
+ return DOM_NO_ERR;
}
/**
@@ -903,15 +1159,62 @@
* The returned node will have its reference count increased. It is
* the responsibility of the caller to unref the node once it has
* finished with it.
+ *
+ * @note: The spec said adoptNode may be light weight than the importNode
+ * because the former need no Node creation. But in our implementation
+ * this can't be ensured. Both adoptNode and importNode create new
+ * nodes using the importing/adopting document's resource manager. So,
+ * generally, the adoptNode and importNode call the same function
+ * dom_document_dup_node.
*/
dom_exception _dom_document_adopt_node(struct dom_document *doc,
struct dom_node *node, struct dom_node **result)
{
- UNUSED(doc);
- UNUSED(node);
- UNUSED(result);
+ dom_exception err;
+ dom_node_internal *n = (dom_node_internal *) node;
+
+ *result = NULL;
- return DOM_NOT_SUPPORTED_ERR;
+ if (n->type == DOM_DOCUMENT_NODE ||
+ n->type == DOM_DOCUMENT_TYPE_NODE) {
+ return DOM_NOT_SUPPORTED_ERR;
+ }
+
+ if (n->type == DOM_ENTITY_NODE ||
+ n->type == DOM_NOTATION_NODE ||
+ n->type == DOM_PROCESSING_INSTRUCTION_NODE ||
+ n->type == DOM_TEXT_NODE ||
+ n->type == DOM_CDATA_SECTION_NODE ||
+ n->type == DOM_COMMENT_NODE) {
+ *result = NULL;
+ return DOM_NO_ERR;
+ }
+
+ /* Support XML when necessary */
+ if (n->type == DOM_ENTITY_REFERENCE_NODE) {
+ return DOM_NOT_SUPPORTED_ERR;
+ }
+
+ err = dom_document_dup_node(doc, node, true, result, DOM_NODE_ADOPTED);
+ if (err != DOM_NO_ERR) {
+ *result = NULL;
+ return err;
+ }
+
+ dom_node_internal *parent = n->parent;
+ dom_node_internal *tmp;
+ if (parent != NULL) {
+ err = dom_node_remove_child(parent, node, (void *) &tmp);
+ if (err != DOM_NO_ERR) {
+ dom_node_unref(*result);
+ *result = NULL;
+ return err;
+ }
+ }
+
+ dom_node_unref(tmp);
+
+ return DOM_NO_ERR;
}
/**
@@ -992,6 +1295,45 @@
return DOM_NOT_SUPPORTED_ERR;
}
+/* Overload protectd virtual functions */
+void _dom_document_destroy(struct dom_node_internal *node)
+{
+ struct dom_document *doc = (struct dom_document *) node;
+
+ if (dom_document_finalise(doc) == true) {
+ doc->alloc(doc, 0, doc->pw);
+ }
+}
+
+dom_exception _dom_document_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret)
+{
+ UNUSED(n);
+ struct dom_document *a;
+
+ a = dom_document_alloc(doc, NULL, sizeof(struct dom_document));
+ if (a == NULL)
+ return DOM_NO_MEM_ERR;
+
+ *ret = (dom_node_internal *) a;
+ dom_node_set_owner(*ret, doc);
+
+ return DOM_NO_ERR;
+}
+
+dom_exception _dom_document_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old)
+{
+ UNUSED(new);
+ UNUSED(old);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/* */
+/* ----------------------------------------------------------------------- */
+/* */
+/* Helper functions */
/**
* Create a DOM string, using a document's allocation context
*
@@ -1013,10 +1355,86 @@
return dom_string_create(doc->alloc, doc->pw, data, len, result);
}
-/* */
-/* ----------------------------------------------------------------------- */
-/* */
+/*
+ * Create a lwc_string
+ *
+ * \param doc The document object
+ * \param data The raw string data
+ * \param len The raw string length
+ * \param result The resturned lwc_string
+ */
+dom_exception dom_document_create_lwcstring(struct dom_document *doc,
+ const uint8_t *data, size_t len, struct lwc_string_s **result)
+{
+ lwc_error lerr;
+ assert(doc->context != NULL);
+
+ lerr = lwc_context_intern(doc->context, (const char *) data, len,
+ result);
+
+ return dom_exception_from_lwc_error(lerr);
+}
+
+/* Simple accessor for lwc_context of this document */
+struct lwc_context_s *dom_document_get_intern_context(
+ struct dom_document *doc)
+{
+ return doc->context;
+}
+
+/* Get the resource manager from the document */
+void dom_document_get_resource_mgr(
+ struct dom_document *doc, struct resource_mgr *rm)
+{
+ rm->alloc = doc->alloc;
+ rm->pw = doc->pw;
+ rm->ctx = doc->context;
+}
+
+/* Simple accessor for allocator data for this document */
+void dom_document_get_allocator(struct dom_document *doc, dom_alloc *al,
+ void **pw)
+{
+ *al = doc->alloc;
+ *pw = doc->pw;
+}
+/*
+ * Create a dom_string from a lwc_string.
+ *
+ * \param doc The document object
+ * \param str The lwc_string object
+ * \param result The retured dom_string
+ */
+dom_exception dom_document_create_string_from_lwcstring(struct dom_document *doc,
+ struct lwc_string_s *str, struct dom_string **result)
+{
+ assert(doc->context != NULL);
+
+ return dom_string_create_from_lwcstring(doc->alloc, doc->pw,
+ doc->context, str, result);
+}
+
+/* Create a hash_table
+ *
+ * \param doc The dom_document
+ * \param chains The number of chains
+ * \param f The hash function
+ * \param ht The returned hash_table
+ */
+dom_exception dom_document_create_hashtable(struct dom_document *doc,
+ size_t chains, hash_func f, struct hash_table **ht)
+{
+ struct hash_table *ret;
+
+ ret = hash_create(chains, f, doc->alloc, doc->pw);
+ if (ret == NULL)
+ return DOM_NO_MEM_ERR;
+
+ *ht = ret;
+ return DOM_NO_ERR;
+}
+
/**
* (De)allocate memory with a document's context
*
@@ -1037,6 +1455,7 @@
* Get a nodelist, creating one if necessary
*
* \param doc The document to get a nodelist for
+ * \param type The type of the NodeList
* \param root Root node of subtree that list applies to
* \param tagname Name of nodes in list (or NULL)
* \param namespace Namespace part of nodes in list (or NULL)
@@ -1048,16 +1467,16 @@
* the responsibility of the caller to unref the list once it has
* finished with it.
*/
-dom_exception dom_document_get_nodelist(struct dom_document *doc,
- struct dom_node_internal *root, struct dom_string *tagname,
- struct dom_string *namespace, struct dom_string *localname,
+dom_exception dom_document_get_nodelist(struct dom_document *doc, nodelist_type type,
+ struct dom_node_internal *root, struct lwc_string_s *tagname,
+ struct lwc_string_s *namespace, struct lwc_string_s *localname,
struct dom_nodelist **list)
{
struct dom_doc_nl *l;
dom_exception err;
for (l = doc->nodelists; l; l = l->next) {
- if (dom_nodelist_match(l->list, root, tagname,
+ if (dom_nodelist_match(l->list, type, root, tagname,
namespace, localname))
break;
}
@@ -1074,7 +1493,7 @@
return DOM_NO_MEM_ERR;
/* Create nodelist */
- err = dom_nodelist_create(doc, root, tagname, namespace,
+ err = dom_nodelist_create(doc, type, root, tagname, namespace,
localname, &l->list);
if (err != DOM_NO_ERR) {
doc->alloc(l, 0, doc->pw);
@@ -1135,97 +1554,152 @@
}
/**
- * Get a namednodemap, creating one if necessary
+ * Find element with certain ID in the subtree rooted at root
*
- * \param doc The document to get a namednodemap for
- * \param head Start of list containing items in map
- * \param type The type of items in map
- * \param map Pointer to location to receive map
- * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion.
- *
- * The returned map will have its reference count increased. It is
- * the responsibility of the caller to unref the map once it has
- * finished with it.
+ * \param root The root element from where we start
+ * \param id The ID of the target element
+ * \param result The result element
*/
-dom_exception dom_document_get_namednodemap(struct dom_document *doc,
- struct dom_node_internal *head, dom_node_type type,
- struct dom_namednodemap **map)
+dom_exception find_element_by_id(dom_node_internal *root,
+ struct lwc_string_s *id, struct dom_element **result)
{
- struct dom_doc_nnm *m;
- dom_exception err;
+ *result = NULL;
+ dom_node_internal *node = root;
- for (m = doc->maps; m; m = m->next) {
- if (dom_namednodemap_match(m->map, head, type))
- break;
- }
+ while (node != NULL) {
+ if (root->type == DOM_ELEMENT_NODE) {
+ lwc_string *real_id;
+ dom_element_get_id((dom_element *) node, &real_id);
+ if (real_id == id) {
+ *result = (dom_element *) node;
+ return DOM_NO_ERR;
+ }
+ }
- if (m != NULL) {
- /* Found an existing map, so use it */
- dom_namednodemap_ref(m->map);
- } else {
- /* No existing map */
+ if (node->first_child != NULL) {
+ /* Has children */
+ node = node->first_child;
+ } else if (node->next != NULL) {
+ /* No children, but has siblings */
+ node = node->next;
+ } else {
+ /* No children or siblings.
+ * Find first unvisited relation. */
+ struct dom_node_internal *parent = node->parent;
- /* Create active map entry */
- m = doc->alloc(NULL, sizeof(struct dom_doc_nnm), doc->pw);
- if (m == NULL)
- return DOM_NO_MEM_ERR;
+ while (parent != root &&
+ node == parent->last_child) {
+ node = parent;
+ parent = parent->parent;
+ }
- /* Create namednodemap */
- err = dom_namednodemap_create(doc, head, type, &m->map);
- if (err != DOM_NO_ERR) {
- doc->alloc(m, 0, doc->pw);
- return err;
+ node = node->next;
}
-
- /* Add to document's list of active namednodemaps */
- m->prev = NULL;
- m->next = doc->maps;
- if (doc->maps)
- doc->maps->prev = m;
- doc->maps = m;
}
- /* Note: the document does not claim a reference on the namednodemap
- * If it did, the map's reference count would never reach zero,
- * and the list would remain indefinitely. This is not a problem as
- * the map notifies the document of its destruction via
- * dom_document_remove_namednodempa. */
-
- *map = m->map;
-
return DOM_NO_ERR;
}
/**
- * Remove a namednodemap
+ * Duplicate a Node
*
- * \param doc The document to remove the map from
- * \param map The map to remove
+ * \param doc The documen
+ * \param node The node to duplicate
+ * \param deep Whether to make a deep copy
+ * \param result The returned node
+ * \param opt Whether this is adopt or import operation
*/
-void dom_document_remove_namednodemap(struct dom_document *doc,
- struct dom_namednodemap *map)
+dom_exception dom_document_dup_node(dom_document *doc, struct dom_node *node,
+ bool deep, struct dom_node **result, dom_node_operation opt)
{
- struct dom_doc_nnm *m;
+ dom_exception err;
+ dom_node_internal *n = (dom_node_internal *) node;
- for (m = doc->maps; m; m = m->next) {
- if (m->map == map)
- break;
+ if (opt == DOM_NODE_ADOPTED && _dom_node_readonly(n))
+ return DOM_NO_MODIFICATION_ALLOWED_ERR;
+
+ if (n->type == DOM_DOCUMENT_NODE ||
+ n->type == DOM_DOCUMENT_TYPE_NODE)
+ return DOM_NOT_SUPPORTED_ERR;
+
+ err = dom_node_alloc(doc, node, result);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ err = dom_node_copy(*result, node);
+ if (err != DOM_NO_ERR) {
+ dom_document_alloc(doc, *result, 0);
+ return err;
}
- if (m == NULL) {
- /* This should never happen; we should probably abort here */
- return;
+ if (n->type == DOM_ATTRIBUTE_NODE) {
+ dom_attr_set_specified((dom_attr *) node, true);
+ deep = true;
}
- /* Remove from list */
- if (m->prev != NULL)
- m->prev->next = m->next;
- else
- doc->maps = m->next;
+ if (n->type == DOM_ENTITY_REFERENCE_NODE) {
+ deep = false;
+ }
- if (m->next != NULL)
- m->next->prev = m->prev;
+ if (n->type == DOM_ELEMENT_NODE) {
+ /* Specified attributes are copyied but not default attributes,
+ * if the document object hold all the default attributes, we
+ * have nothing to do here */
+ }
- /* And free item */
- doc->alloc(m, 0, doc->pw);
+ if (opt == DOM_NODE_ADOPTED && (n->type == DOM_ENTITY_NODE ||
+ n->type == DOM_NOTATION_NODE)) {
+ /* We did not support XML now */
+ return DOM_NOT_SUPPORTED_ERR;
+ }
+
+ dom_node_internal *child, *r;
+ if (deep == true) {
+ child = ((dom_node_internal *) node)->first_child;
+ while (child != NULL) {
+ err = dom_document_import_node(doc, child, deep, (void *) &r);
+ if (err != DOM_NO_ERR) {
+ dom_document_alloc(doc, *result, 0);
+ return err;
+ }
+
+ err = dom_node_append_child(*result, r, (void *) &r);
+ if (err != DOM_NO_ERR) {
+ dom_document_alloc(doc, *result, 0);
+ dom_node_unref(r);
+ return err;
+ }
+ dom_node_unref(r);
+
+ child = child->next;
+ }
+ }
+
+ /* Call the dom_user_data_handlers */
+ dom_user_data *ud;
+ ud = n->user_data;
+ while (ud != NULL) {
+ if (ud->handler != NULL)
+ ud->handler(opt, ud->key, ud->data,
+ node, *result);
+ ud = ud->next;
+ }
+
+ return DOM_NO_ERR;
}
+
+/**
+ * Try to destory the document.
+ * Delete the document if:
+ * 1. The refcnt reach zero
+ * 2. The pending list is empty
+ *
+ * else, do nothing.
+ */
+void dom_document_try_destroy(struct dom_document *doc)
+{
+ if (doc->base.refcnt != 0 || doc->base.parent != NULL)
+ return;
+
+ _dom_document_destroy((dom_node_internal *) doc);
+}
Index: src/core/document_type.c
===================================================================
--- src/core/document_type.c (revision 8909)
+++ src/core/document_type.c (working copy)
@@ -4,15 +4,20 @@
* http://www.opensource.org/licenses/mit-license.php
* Copyright 2007 John-Mark Bell <jmb(a)netsurf-browser.org>
* Copyright 2007 James Shaw <jshaw(a)netsurf-browser.org>
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
*/
+#include <assert.h>
+
#include <dom/core/document_type.h>
-#include <dom/core/string.h>
#include <dom/bootstrap/implpriv.h>
+#include "core/string.h"
#include "core/document_type.h"
#include "core/node.h"
#include "utils/utils.h"
+#include "utils/namespace.h"
+#include "utils/resource_mgr.h"
/**
* DOM DocumentType node
@@ -20,12 +25,13 @@
struct dom_document_type {
struct dom_node_internal base; /**< Base node */
+ struct dom_implementation *impl; /**< Owning implementation */
+
/** \todo other members */
struct dom_string *public_id; /**< Doctype public ID */
struct dom_string *system_id; /**< Doctype system ID */
- dom_alloc alloc; /**< Memory (de)allocation function */
- void *pw; /**< Pointer to private data */
+ struct resource_mgr res;
};
static struct dom_document_type_vtable document_type_vtable = {
@@ -35,6 +41,15 @@
DOM_DOCUMENT_TYPE_VTABLE
};
+static struct dom_node_protect_vtable dt_protect_vtable = {
+ DOM_DT_PROTECT_VTABLE
+};
+
+/* */
+/*----------------------------------------------------------------------*/
+/* */
+/* Constructors and destructors */
+
/**
* Create a document type node
*
@@ -52,7 +67,8 @@
*/
dom_exception dom_document_type_create(struct dom_string *qname,
struct dom_string *public_id, struct dom_string *system_id,
- dom_alloc alloc, void *pw, struct dom_document_type **doctype)
+ dom_alloc alloc, void *pw, struct lwc_context_s *ctx,
+ struct dom_document_type **doctype)
{
struct dom_document_type *result;
dom_exception err;
@@ -62,29 +78,13 @@
if (result == NULL)
return DOM_NO_MEM_ERR;
- /* Initialise base node */
- err = dom_node_initialise(&result->base, NULL, DOM_DOCUMENT_TYPE_NODE,
- qname, NULL, NULL, NULL);
- if (err != DOM_NO_ERR) {
- alloc(result, 0, pw);
- return err;
- }
-
/* Initialise the vtable */
result->base.base.vtable = &document_type_vtable;
- result->base.destroy = &dom_document_type_destroy;
+ result->base.vtable = &dt_protect_vtable;
+
+ err = dom_document_type_initialise(result, qname, public_id, system_id,
+ alloc, pw, ctx);
- /* Get public and system IDs */
- dom_string_ref(public_id);
- result->public_id = public_id;
-
- dom_string_ref(system_id);
- result->system_id = system_id;
-
- /* Fill in allocation information */
- result->alloc = alloc;
- result->pw = pw;
-
*doctype = result;
return DOM_NO_ERR;
@@ -102,17 +102,101 @@
struct dom_document_type *doctype =
(struct dom_document_type *)doctypenode;
- /* Finish with public and system IDs */
- dom_string_unref(doctype->system_id);
- dom_string_unref(doctype->public_id);
-
/* Finalise base class */
- dom_node_finalise(doctype->base.owner, &doctype->base);
+ dom_document_type_finalise(doctype);
/* Free doctype */
- doctype->alloc(doctype, 0, doctype->pw);
+ doctype->res.alloc(doctype, 0, doctype->res.pw);
}
+dom_exception dom_document_type_initialise(struct dom_document_type *doctype,
+ struct dom_string *qname, struct dom_string *public_id,
+ struct dom_string *system_id, dom_alloc alloc, void *pw,
+ struct lwc_context_s *ctx)
+{
+ dom_exception err;
+
+ dom_string *prefix, *localname;
+ err = _dom_namespace_split_qname(qname, &prefix, &localname);
+ if (err != DOM_NO_ERR) {
+ alloc(doctype, 0, pw);
+ return err;
+ }
+
+ lwc_string *lprefix = NULL, *lname = NULL;
+ if (prefix != NULL) {
+ err = dom_string_intern(prefix, ctx, &lprefix);
+ if (err != DOM_NO_ERR) {
+ dom_string_unref(prefix);
+ dom_string_unref(localname);
+ alloc(doctype, 0, pw);
+ return err;
+ }
+ }
+
+ if (localname != NULL) {
+ err = dom_string_intern(localname, ctx, &lname);
+ if (err != DOM_NO_ERR) {
+ dom_string_unref(prefix);
+ dom_string_unref(localname);
+ if (lprefix != NULL)
+ lwc_context_string_unref(ctx, lprefix);
+ alloc(doctype, 0, pw);
+ return err;
+ }
+ }
+
+ /* TODO: I should figure out how the namespaceURI can be got */
+
+ /* Initialise base node */
+ err = dom_node_initialise_generic(&doctype->base, NULL, alloc, pw, ctx,
+ DOM_DOCUMENT_TYPE_NODE, lname, NULL, NULL, lprefix);
+ if (err != DOM_NO_ERR) {
+ alloc(doctype, 0, pw);
+ return err;
+ }
+
+ /* Get public and system IDs */
+ if (public_id != NULL)
+ dom_string_ref(public_id);
+ doctype->public_id = public_id;
+
+ if (system_id != NULL)
+ dom_string_ref(system_id);
+ doctype->system_id = system_id;
+
+ if (prefix != NULL)
+ dom_string_unref(prefix);
+ if (localname != NULL)
+ dom_string_unref(localname);
+
+ /* Fill in allocation information */
+ doctype->res.alloc = alloc;
+ doctype->res.pw = pw;
+ doctype->res.ctx = ctx;
+
+ return DOM_NO_ERR;
+}
+
+/* The constructor function of dom_document_type */
+void dom_document_type_finalise(struct dom_document_type *doctype)
+{
+ if (doctype->public_id != NULL)
+ dom_string_unref(doctype->public_id);
+ if (doctype->system_id != NULL)
+ dom_string_unref(doctype->system_id);
+
+ assert(doctype->base.owner != NULL || doctype->base.user_data == NULL);
+
+ dom_node_finalise_generic(&doctype->base, doctype->res.alloc, doctype->res.pw,
+ doctype->res.ctx);
+}
+
+/* */
+/*----------------------------------------------------------------------*/
+/* */
+/* Virtual functions */
+
/**
* Retrieve a document type's name
*
@@ -238,3 +322,52 @@
return DOM_NOT_SUPPORTED_ERR;
}
+/* Overload protected virtual functions */
+void _dom_dt_destroy(struct dom_node_internal *node)
+{
+ dom_document_type_destroy(node);
+}
+
+dom_exception _dom_dt_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret)
+{
+ UNUSED(doc);
+ UNUSED(n);
+ UNUSED(ret);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+dom_exception _dom_dt_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old)
+{
+ UNUSED(new);
+ UNUSED(old);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/* */
+/*----------------------------------------------------------------------*/
+/* */
+/* Helper functions */
+
+/* Get the resource manager of this object */
+void dom_document_type_get_resource_mgr(
+ struct dom_document_type *dt, struct resource_mgr *rm)
+{
+ rm->alloc = dt->res.alloc;
+ rm->pw = dt->res.pw;
+ rm->ctx = dt->res.ctx;
+}
+
+/**
+ * Get the implementation which created this dom_document_type
+ *
+ * \param dt The document type object
+ */
+struct dom_implementation *dom_document_type_get_impl(
+ struct dom_document_type *dt)
+{
+ return dt->impl;
+}
Index: src/core/attr.h
===================================================================
--- src/core/attr.h (revision 8909)
+++ src/core/attr.h (working copy)
@@ -13,26 +13,31 @@
struct dom_document;
struct dom_string;
struct dom_type_info;
+struct lwc_string_s;
dom_exception dom_attr_create(struct dom_document *doc,
- struct dom_string *name, struct dom_string *namespace,
- struct dom_string *prefix, struct dom_attr **result);
+ struct lwc_string_s *name, struct lwc_string_s *namespace,
+ struct lwc_string_s *prefix, bool specified,
+ struct dom_attr **result);
void dom_attr_destroy(struct dom_document *doc, struct dom_attr *attr);
-/* The virtual destroy function */
-void _dom_attr_destroy(dom_node_internal *node);
+dom_exception dom_attr_initialise(struct dom_attr *a,
+ struct dom_document *doc, struct lwc_string_s *name,
+ struct lwc_string_s *namespace, struct lwc_string_s *prefix,
+ bool specified, struct dom_attr **result);
+void dom_attr_finalise(dom_document *doc, struct dom_attr *attr);
-/* Virtual functions for dom attr */
+/* Virtual functions for dom_attr */
dom_exception _dom_attr_get_name(struct dom_attr *attr,
- struct dom_string **result);
+ struct dom_string **result);
dom_exception _dom_attr_get_specified(struct dom_attr *attr, bool *result);
dom_exception _dom_attr_get_value(struct dom_attr *attr,
- struct dom_string **result);
+ struct dom_string **result);
dom_exception _dom_attr_set_value(struct dom_attr *attr,
- struct dom_string *value);
+ struct dom_string *value);
dom_exception _dom_attr_get_owner(struct dom_attr *attr,
- struct dom_element **result);
+ struct dom_element **result);
dom_exception _dom_attr_get_schema_type_info(struct dom_attr *attr,
- struct dom_type_info **result);
+ struct dom_type_info **result);
dom_exception _dom_attr_is_id(struct dom_attr *attr, bool *result);
#define DOM_ATTR_VTABLE \
@@ -44,4 +49,73 @@
_dom_attr_get_schema_type_info, \
_dom_attr_is_id
+/* Overloading dom_node functions */
+dom_exception _dom_attr_get_node_value(dom_node_internal *node,
+ struct dom_string **result);
+dom_exception _dom_attr_clone_node(dom_node_internal *node, bool deep,
+ dom_node_internal **result);
+dom_exception _dom_attr_set_prefix(dom_node_internal *node,
+ struct dom_string *prefix);
+dom_exception _dom_attr_normalize(dom_node_internal *node);
+dom_exception _dom_attr_lookup_prefix(dom_node_internal *node,
+ struct dom_string *namespace, struct dom_string **result);
+dom_exception _dom_attr_is_default_namespace(dom_node_internal *node,
+ struct dom_string *namespace, bool *result);
+dom_exception _dom_attr_lookup_namespace(dom_node_internal *node,
+ struct dom_string *prefix, struct dom_string **result);
+#define DOM_NODE_VTABLE_ATTR \
+ _dom_node_get_node_name, \
+ _dom_attr_get_node_value, /*overload*/\
+ _dom_node_set_node_value, \
+ _dom_node_get_node_type, \
+ _dom_node_get_parent_node, \
+ _dom_node_get_child_nodes, \
+ _dom_node_get_first_child, \
+ _dom_node_get_last_child, \
+ _dom_node_get_previous_sibling, \
+ _dom_node_get_next_sibling, \
+ _dom_node_get_attributes, \
+ _dom_node_get_owner_document, \
+ _dom_node_insert_before, \
+ _dom_node_replace_child, \
+ _dom_node_remove_child, \
+ _dom_node_append_child, \
+ _dom_node_has_child_nodes, \
+ _dom_attr_clone_node, /*overload*/\
+ _dom_node_normalize, \
+ _dom_node_is_supported, \
+ _dom_node_get_namespace, \
+ _dom_node_get_prefix, \
+ _dom_attr_set_prefix, /*overload*/\
+ _dom_node_get_local_name, \
+ _dom_node_has_attributes, \
+ _dom_node_get_base, \
+ _dom_node_compare_document_position, \
+ _dom_node_get_text_content, \
+ _dom_node_set_text_content, \
+ _dom_node_is_same, \
+ _dom_attr_lookup_prefix, /*overload*/\
+ _dom_attr_is_default_namespace, /*overload*/\
+ _dom_attr_lookup_namespace, /*overload*/\
+ _dom_node_is_equal, \
+ _dom_node_get_feature, \
+ _dom_node_set_user_data, \
+ _dom_node_get_user_data
+
+/* The protected virtual functions */
+void _dom_attr_destroy(dom_node_internal *node);
+dom_exception _dom_attr_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret);
+dom_exception _dom_attr_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old);
+
+#define DOM_ATTR_PROTECT_VTABLE \
+ _dom_attr_destroy, \
+ _dom_attr_alloc, \
+ _dom_attr_copy
+
+
+inline void dom_attr_set_isid(struct dom_attr *attr, bool is_id);
+inline void dom_attr_set_specified(struct dom_attr *attr, bool specified);
+
#endif
Index: src/core/document.h
===================================================================
--- src/core/document.h (revision 8909)
+++ src/core/document.h (working copy)
@@ -12,10 +12,15 @@
#include <stddef.h>
#include <dom/core/node.h>
-#include <dom/core/string.h>
+#include "core/string.h"
#include "core/node.h"
+#include "core/nodelist.h"
+#include "utils/hashtable.h"
+#include "utils/resource_mgr.h"
+#include "utils/list.h"
+
struct dom_document;
struct dom_namednodemap;
struct dom_node;
@@ -32,7 +37,6 @@
struct dom_configuration;
struct dom_doc_nl;
-struct dom_doc_nnm;
/**
* DOM document
@@ -46,14 +50,46 @@
struct dom_doc_nl *nodelists; /**< List of active nodelists */
- struct dom_doc_nnm *maps; /**< List of active namednodemaps */
+ struct dom_string **nodenames; /**< Various nodenames */
- struct dom_string **nodenames; /**< Interned nodenames */
+ struct dom_string *uri; /**< The uri of this document */
+ struct lwc_context_s *context; /**< The internment context */
+
dom_alloc alloc; /**< Memory (de)allocation function */
void *pw; /**< Pointer to client data */
+
+ struct list_entry pending_nodes; /**< The deletion pending list */
};
+
+/* Initialise the document module */
+dom_exception dom_document_module_initialise(dom_alloc alloc, void *pw);
+
+/* Finalise the document module */
+dom_exception dom_document_module_finalise(void);
+
+
+/* Initialise the document */
+dom_exception dom_document_initialise(struct dom_document *doc,
+ struct dom_implementation *impl, dom_alloc alloc, void *pw,
+ struct lwc_context_s *ctx);
+
+/* Finalise the document */
+bool dom_document_finalise(struct dom_document *doc);
+
+/* Create a dom_string from C string */
+dom_exception dom_document_create_string(struct dom_document *doc,
+ const uint8_t *data, size_t len, struct dom_string **result);
+/* Create a lwc_string from C string */
+dom_exception dom_document_create_lwcstring(struct dom_document *doc,
+ const uint8_t *data, size_t len, struct lwc_string_s **result);
+/* Create a dom_string from a lwc_string */
+dom_exception dom_document_create_string_from_lwcstring(struct dom_document *doc,
+ struct lwc_string_s *str, struct dom_string **result);
+
+
+/* Begin the virtual functions */
dom_exception _dom_document_get_doctype(struct dom_document *doc,
struct dom_document_type **result);
dom_exception _dom_document_get_implementation(struct dom_document *doc,
@@ -156,34 +192,60 @@
_dom_document_get_dom_config, \
_dom_document_normalize, \
_dom_document_rename_node
+/* End of vtable */
-/* Initialise the document module */
-dom_exception _dom_document_initialise(dom_alloc alloc, void *pw);
-/* Finalise the document module */
-dom_exception _dom_document_finalise(void);
+/* Following comes the protected vtable */
+void _dom_document_destroy(struct dom_node_internal *node);
+dom_exception _dom_document_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret);
+dom_exception _dom_document_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old);
-/* Destroy a document */
-void dom_document_destroy(struct dom_node_internal *dnode);
+#define DOM_DOCUMENT_PROTECT_VTABLE \
+ _dom_document_destroy, \
+ _dom_document_alloc, \
+ _dom_document_copy
+
+/*---------------------------- Helper functions ---------------------------*/
+/* Try to destroy the document:
+ * When the refcnt is zero and the pending list is empty, we can destroy this
+ * document.
+ */
+void dom_document_try_destroy(struct dom_document *doc);
/* (De)allocate memory */
void *dom_document_alloc(struct dom_document *doc, void *ptr, size_t size);
+/* Get the internment context */
+inline struct lwc_context_s *dom_document_get_intern_context(
+ struct dom_document *doc);
+
+/* Get the resource manager inside this document, a resource manager
+ * is an object which contain the memory allocator/intern string context,
+ * with which we can alloc strings or intern strings */
+void dom_document_get_resource_mgr(
+ struct dom_document *doc, struct resource_mgr *rm);
+
+/* Get the internal allocator and its pointer */
+inline void dom_document_get_allocator(struct dom_document *doc, dom_alloc *al,
+ void **pw);
+
+/* Create a hash_table */
+dom_exception dom_document_create_hashtable(struct dom_document *doc,
+ size_t chains, hash_func f, struct hash_table **ht);
+
/* Get a nodelist, creating one if necessary */
-dom_exception dom_document_get_nodelist(struct dom_document *doc,
- struct dom_node_internal *root, struct dom_string *tagname,
- struct dom_string *namespace, struct dom_string *localname,
+dom_exception dom_document_get_nodelist(struct dom_document *doc, nodelist_type type,
+ struct dom_node_internal *root, struct lwc_string_s *tagname,
+ struct lwc_string_s *namespace, struct lwc_string_s *localname,
struct dom_nodelist **list);
/* Remove a nodelist */
void dom_document_remove_nodelist(struct dom_document *doc,
struct dom_nodelist *list);
-/* Get a namednodemap, creating one if necessary */
-dom_exception dom_document_get_namednodemap(struct dom_document *doc,
- struct dom_node_internal *head, dom_node_type type,
- struct dom_namednodemap **map);
-/* Remove a namednodemap */
-void dom_document_remove_namednodemap(struct dom_document *doc,
- struct dom_namednodemap *map);
+/* Find element with certain ID in the subtree rooted at root */
+dom_exception find_element_by_id(dom_node_internal *root,
+ struct lwc_string_s *id, struct dom_element **result);
#endif
Index: src/core/document_type.h
===================================================================
--- src/core/document_type.h (revision 8909)
+++ src/core/document_type.h (working copy)
@@ -9,9 +9,16 @@
#define dom_internal_core_document_type_h_
struct dom_document_type;
+struct resource_mgr;
+struct dom_implementation;
/* Destroy a document type */
void dom_document_type_destroy(struct dom_node_internal *doctypenode);
+dom_exception dom_document_type_initialise(struct dom_document_type *doctype,
+ struct dom_string *qname, struct dom_string *public_id,
+ struct dom_string *system_id, dom_alloc alloc, void *pw,
+ struct lwc_context_s *ctx);
+void dom_document_type_finalise(struct dom_document_type *doctype);
/* The virtual functions of DocumentType */
dom_exception _dom_document_type_get_name(struct dom_document_type *doc_type,
@@ -40,5 +47,22 @@
_dom_document_type_get_system_id, \
_dom_document_type_get_internal_subset
+/* Following comes the protected vtable */
+void _dom_dt_destroy(struct dom_node_internal *node);
+dom_exception _dom_dt_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret);
+dom_exception _dom_dt_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old);
+
+#define DOM_DT_PROTECT_VTABLE \
+ _dom_dt_destroy, \
+ _dom_dt_alloc, \
+ _dom_dt_copy
+
+/* Helper functions */
+void dom_document_type_get_resource_mgr(
+ struct dom_document_type *dt, struct resource_mgr *rm);
+struct dom_implementation *dom_document_type_get_impl(
+ struct dom_document_type *dt);
+
#endif
-
Index: src/core/implementation.c
===================================================================
--- src/core/implementation.c (revision 8909)
+++ src/core/implementation.c (working copy)
@@ -59,8 +59,6 @@
* \param public_id The external subset public identifier
* \param system_id The external subset system identifier
* \param doctype Pointer to location to receive result
- * \param alloc Memory (de)allocation function
- * \param pw Pointer to client-specific private data
* \return DOM_NO_ERR on success,
* DOM_INVALID_CHARACTER_ERR if ::qname is invalid,
* DOM_NAMESPACE_ERR if ::qname is malformed,
@@ -79,11 +77,11 @@
dom_exception dom_implementation_create_document_type(
struct dom_implementation *impl, struct dom_string *qname,
struct dom_string *public_id, struct dom_string *system_id,
- struct dom_document_type **doctype,
- dom_alloc alloc, void *pw)
+ dom_alloc alloc, void *pw, struct lwc_context_s *ctx,
+ struct dom_document_type **doctype)
{
return impl->create_document_type(impl, qname, public_id, system_id,
- doctype, alloc, pw);
+ alloc, pw, ctx, doctype);
}
/**
@@ -94,8 +92,6 @@
* \param qname The qualified name of the document element
* \param doctype The type of document to create
* \param doc Pointer to location to receive result
- * \param alloc Memory (de)allocation function
- * \param pw Pointer to client-specific private data
* \return DOM_NO_ERR on success,
* DOM_INVALID_CHARACTER_ERR if ::qname is invalid,
* DOM_NAMESPACE_ERR if ::qname is malformed, or if ::qname
@@ -125,11 +121,11 @@
struct dom_implementation *impl,
struct dom_string *namespace, struct dom_string *qname,
struct dom_document_type *doctype,
- struct dom_document **doc,
- dom_alloc alloc, void *pw)
+ dom_alloc alloc, void *pw, struct lwc_context_s *ctx,
+ struct dom_document **doc)
{
- return impl->create_document(impl, namespace, qname, doctype, doc,
- alloc, pw);
+ return impl->create_document(impl, namespace, qname, doctype, alloc,
+ pw, ctx, doc);
}
/**
@@ -140,8 +136,6 @@
* \param feature The requested feature
* \param version The version number of the feature
* \param object Pointer to location to receive object
- * \param alloc Memory (de)allocation function
- * \param pw Pointer to client-specific private data
* \return DOM_NO_ERR.
*
* Any memory allocated by this call should be allocated using
@@ -150,8 +144,7 @@
dom_exception dom_implementation_get_feature(
struct dom_implementation *impl,
struct dom_string *feature, struct dom_string *version,
- void **object,
- dom_alloc alloc, void *pw)
+ void **object)
{
- return impl->get_feature(impl, feature, version, object, alloc, pw);
+ return impl->get_feature(impl, feature, version, object);
}
Index: src/core/node.c
===================================================================
--- src/core/node.c (revision 8909)
+++ src/core/node.c (working copy)
@@ -3,6 +3,7 @@
* Licensed under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
* Copyright 2007 John-Mark Bell <jmb(a)netsurf-browser.org>
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
*/
#include <assert.h>
@@ -10,9 +11,15 @@
#include <stdio.h>
#include <dom/core/attr.h>
+#include <dom/core/text.h>
#include <dom/core/document.h>
-#include <dom/core/string.h>
+#include <dom/core/namednodemap.h>
+#include <dom/core/nodelist.h>
+#include <dom/core/implementation.h>
+#include <dom/core/document_type.h>
+#include "core/string.h"
+#include "core/namednodemap.h"
#include "core/attr.h"
#include "core/cdatasection.h"
#include "core/comment.h"
@@ -25,6 +32,7 @@
#include "core/pi.h"
#include "core/text.h"
#include "utils/utils.h"
+#include "utils/resource_mgr.h"
static bool _dom_node_permitted_child(const dom_node_internal *parent,
const dom_node_internal *child);
@@ -48,6 +56,16 @@
DOM_NODE_VTABLE
};
+static struct dom_node_protect_vtable node_protect_vtable = {
+ DOM_NODE_PROTECT_VTABLE
+};
+
+
+/* */
+/* ---------------------------------------------------------------------*/
+/* */
+/* The constructor and destructor of this object */
+
/**
* Create a DOM node and compose the vtable
*
@@ -62,7 +80,7 @@
return NULL;
node->base.vtable = &node_vtable;
- node->destroy = _dom_node_destroy;
+ node->vtable = &node_protect_vtable;
return node;
}
@@ -86,9 +104,6 @@
bool null_owner_permitted = (node->type == DOM_DOCUMENT_NODE ||
node->type == DOM_DOCUMENT_TYPE_NODE);
- /* This function simply acts as a central despatcher
- * for type-specific destructors. */
-
assert(null_owner_permitted || owner != NULL);
if (!null_owner_permitted) {
@@ -98,56 +113,19 @@
dom_node_ref(owner);
}
-/* This type dependent switch is not necessary from now.
- But I still keep them for a while untill all the functions works well.
- switch (node->type) {
- case DOM_ELEMENT_NODE:
- dom_element_destroy(owner, (struct dom_element *) node);
- break;
- case DOM_ATTRIBUTE_NODE:
- dom_attr_destroy(owner, (struct dom_attr *) node);
- break;
- case DOM_TEXT_NODE:
- dom_text_destroy(owner, (struct dom_text *) node);
- break;
- case DOM_CDATA_SECTION_NODE:
- dom_cdata_section_destroy(owner,
- (struct dom_cdata_section *) node);
- break;
- case DOM_ENTITY_REFERENCE_NODE:
- dom_entity_reference_destroy(owner,
- (struct dom_entity_reference *) node);
- break;
- case DOM_ENTITY_NODE:
- break;
- case DOM_PROCESSING_INSTRUCTION_NODE:
- dom_processing_instruction_destroy(owner,
- (struct dom_processing_instruction *) node);
- break;
- case DOM_COMMENT_NODE:
- dom_comment_destroy(owner, (struct dom_comment *) node);
- break;
- case DOM_DOCUMENT_NODE:
- dom_document_destroy((struct dom_document *) node);
- break;
- case DOM_DOCUMENT_TYPE_NODE:
- dom_document_type_destroy((struct dom_document_type *) node);
- break;
- case DOM_DOCUMENT_FRAGMENT_NODE:
- dom_document_fragment_destroy(owner,
- (struct dom_document_fragment *) node);
- break;
- case DOM_NOTATION_NODE:
- break;
- }
-*/
+ /* Finalise this node, this should also destroy all the child nodes. */
+ dom_node_finalise(owner, node);
+
if (!null_owner_permitted) {
- /* Release the reference we claimed on the document. If this
- * is the last reference held on the document and the list
- * of nodes pending deletion is empty, then the document will
+ /* Release the reference we claimed on the document. If this
+ * is the last reference held on the document and the list
+ * of nodes pending deletion is empty, then the document will
* be destroyed. */
dom_node_unref(owner);
}
+
+ /* Release our memory */
+ dom_document_alloc(owner, node, 0);
}
/**
@@ -167,11 +145,61 @@
*/
dom_exception dom_node_initialise(dom_node_internal *node,
struct dom_document *doc, dom_node_type type,
- struct dom_string *name, struct dom_string *value,
- struct dom_string *namespace, struct dom_string *prefix)
+ struct lwc_string_s *name, struct dom_string *value,
+ struct lwc_string_s *namespace, struct lwc_string_s *prefix)
{
+ lwc_context *ctx;
+ dom_alloc alloc;
+ void *pw;
+ dom_exception err;
+
+ ctx = dom_document_get_intern_context(doc);
+ /* The lwc_context for a document newer can be NULL */
+ assert(ctx != NULL);
+
+ dom_document_get_allocator(doc, &alloc, &pw);
+
+ err = dom_node_initialise_generic(node, doc, alloc, pw, ctx, type, name,
+ value, namespace, prefix);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Initialise a DOM node
+ *
+ * \param node The node to initialise
+ * \param doc The document object
+ * \param alloc The memory allocator
+ * \param pw The allocator private pointer data
+ * \param ctx The intern context
+ * \param type The node type required
+ * \param name The node (local) name, or NULL
+ * \param value The node value, or NULL
+ * \param namespace Namespace URI to use for node, or NULL
+ * \param prefix Namespace prefix to use for node, or NULL
+ * \return DOM_NO_ERR on success.
+ *
+ * ::name, ::value, ::namespace, and ::prefix will have their reference
+ * counts increased.
+ */
+dom_exception dom_node_initialise_generic(
+ struct dom_node_internal *node, struct dom_document *doc,
+ dom_alloc alloc, void *pw, struct lwc_context_s *ctx,
+ dom_node_type type, struct lwc_string_s *name,
+ struct dom_string *value, struct lwc_string_s *namespace,
+ struct lwc_string_s *prefix)
+{
+ UNUSED(alloc);
+ UNUSED(pw);
+
+ assert(ctx != NULL);
+ node->owner = doc;
+
if (name != NULL)
- dom_string_ref(name);
+ lwc_context_string_ref(ctx, name);
node->name = name;
if (value != NULL)
@@ -204,15 +232,14 @@
* deletion. This list will not be forcibly emptied, as it contains
* those nodes (and their sub-trees) in use by client code.
*/
- node->owner = doc;
if (namespace != NULL) {
- dom_string_ref(namespace);
+ lwc_context_string_ref(ctx, namespace);
}
node->namespace = namespace;
if (prefix != NULL) {
- dom_string_ref(prefix);
+ lwc_context_string_ref(ctx, prefix);
}
node->prefix = prefix;
@@ -220,6 +247,12 @@
node->refcnt = 1;
+ list_init(&node->pending_list);
+ if (node->type != DOM_DOCUMENT_NODE) {
+ /* A Node should be in the pending list when it is created */
+ dom_node_mark_pending(node);
+ }
+
return DOM_NO_ERR;
}
@@ -234,31 +267,63 @@
*/
void dom_node_finalise(struct dom_document *doc, dom_node_internal *node)
{
+ lwc_context *ctx;
+ dom_alloc alloc;
+ void *pw;
+
+ ctx = dom_document_get_intern_context(doc);
+ /* The lwc_context for a document newer can be NULL */
+ assert(ctx != NULL);
+
+ dom_document_get_allocator(doc, &alloc, &pw);
+
+ dom_node_finalise_generic(node, alloc, pw, ctx);
+}
+
+/**
+ * Finalise a DOM node
+ *
+ * \param node The node to finalise
+ * \param alloc The allocator
+ * \param pw The allocator private data
+ * \param ctx The intern string context
+ *
+ * The contents of ::node will be cleaned up. ::node will not be freed.
+ * All children of ::node should have been removed prior to finalisation.
+ */
+void dom_node_finalise_generic(dom_node_internal *node, dom_alloc alloc,
+ void *pw, struct lwc_context_s *ctx)
+{
struct dom_user_data *u, *v;
- /* Standalone DocumentType nodes may not have user data attached */
- assert(node->type != DOM_DOCUMENT_TYPE_NODE ||
- node->user_data == NULL);
+ UNUSED(alloc);
+ UNUSED(pw);
+ assert(ctx != NULL);
+
/* Destroy user data */
for (u = node->user_data; u != NULL; u = v) {
v = u->next;
-
dom_string_unref(u->key);
-
- dom_document_alloc(doc, u, 0);
+ alloc(u, 0, pw);
}
+ node->user_data = NULL;
if (node->prefix != NULL)
- dom_string_unref(node->prefix);
+ lwc_context_string_unref(ctx, node->prefix);
if (node->namespace != NULL)
- dom_string_unref(node->namespace);
+ lwc_context_string_unref(ctx, node->namespace);
- /** \todo check if this node is in list of nodes pending deletion.
- * If so, it must be removed from the list, so the document gets
- * destroyed once the list is empty (and no longer referenced) */
- node->owner = NULL;
+ /* Destroy all the child nodes of this node */
+ struct dom_node_internal *p = node->first_child;
+ struct dom_node_internal *n = NULL;
+ while (p != NULL) {
+ n = p->next;
+ p->parent = NULL;
+ dom_node_try_destroy(p);
+ p = n;
+ }
/* Paranoia */
node->next = NULL;
@@ -271,7 +336,19 @@
dom_string_unref(node->value);
if (node->name != NULL)
- dom_string_unref(node->name);
+ lwc_context_string_unref(ctx, node->name);
+
+ /* Detach from the pending list, if we are in it */
+ if (node->pending_list.prev != &node->pending_list) {
+ assert (node->pending_list.next != &node->pending_list);
+ list_del(&node->pending_list);
+ if (node->owner != NULL && node->type != DOM_DOCUMENT_NODE) {
+ /* Deleting this node from the pending list may cause the list to be
+ * null and we should try to destroy the document.
+ */
+ dom_document_try_destroy(node->owner);
+ }
+ }
}
/**
@@ -284,6 +361,11 @@
node->refcnt++;
}
+/* */
+/* ---------------------------------------------------------------------*/
+/* */
+/* The public virtual function of this interface Node */
+
/**
* Release a reference on a DOM node
*
@@ -291,15 +373,20 @@
*
* If the reference count reaches zero and the node is not part of any
* document, any memory claimed by the node will be released.
+ *
+ * If the parent of the node is NULL but the reference count does not reach
+ * zero, this means we should put this node to the document's deletion pending
+ * list. When the refcnt reach zero, we delete it.
*/
void _dom_node_unref(dom_node_internal *node)
{
+ if (node == NULL)
+ return;
+
if (node->refcnt > 0)
node->refcnt--;
- if (node->refcnt == 0 && node->parent == NULL) {
- dom_node_destroy(node);
- }
+ dom_node_try_destroy(node);
}
/**
@@ -316,50 +403,84 @@
dom_exception _dom_node_get_node_name(dom_node_internal *node,
struct dom_string **result)
{
- struct dom_string *node_name;
+ struct dom_string *node_name, *temp;
+ dom_document *doc;
+ dom_exception err;
+ struct resource_mgr rm;
+ doc = node->owner;
+ /* Document Node and DocumentType Node can have no owner */
+ assert(node->type == DOM_DOCUMENT_TYPE_NODE ||
+ node->type == DOM_DOCUMENT_NODE ||
+ doc != NULL);
+
assert(node->name != NULL);
+ if (doc != NULL) {
+ dom_document_get_resource_mgr(doc, &rm);
+ } else if (node->type == DOM_DOCUMENT_TYPE_NODE) {
+ dom_document_type_get_resource_mgr((dom_document_type *) node, &rm);
+ }
+
/* If this node was created using a namespace-aware method and
* has a defined prefix, then nodeName is a QName comprised
* of prefix:name. */
- if ((node->type == DOM_ELEMENT_NODE ||
- node->type == DOM_ATTRIBUTE_NODE) &&
- node->prefix != NULL) {
+ if(node->prefix != NULL) {
struct dom_string *colon;
- dom_exception err;
- err = dom_document_create_string(node->owner,
+ err = resource_mgr_create_string(&rm,
(const uint8_t *) ":", SLEN(":"), &colon);
if (err != DOM_NO_ERR) {
return err;
}
+ /* Make a temp prefix dom_string */
+ err = resource_mgr_create_string_from_lwcstring(&rm,
+ node->prefix, &temp);
+ if (err != DOM_NO_ERR) {
+ dom_string_unref(colon);
+ return err;
+ }
+
/* Prefix + : */
- err = dom_string_concat(node->prefix, colon, &node_name);
+ err = dom_string_concat(temp, colon, &node_name);
if (err != DOM_NO_ERR) {
+ dom_string_unref(temp);
dom_string_unref(colon);
return err;
}
+ /*Finished with temp*/
+ dom_string_unref(temp);
/* Finished with colon */
dom_string_unref(colon);
+ /* Make a temp name dom_string */
+ err = resource_mgr_create_string_from_lwcstring(&rm,
+ node->name, &temp);
+ if (err != DOM_NO_ERR) {
+ return err;
+ }
/* Prefix + : + Localname */
- err = dom_string_concat(node_name, node->name, &colon);
+ err = dom_string_concat(node_name, temp, &colon);
if (err != DOM_NO_ERR) {
+ dom_string_unref(temp);
dom_string_unref(node_name);
return err;
}
+ /* Finished with temp */
+ dom_string_unref(temp);
/* Finished with intermediate node name */
dom_string_unref(node_name);
node_name = colon;
} else {
- dom_string_ref(node->name);
-
- node_name = node->name;
+ err = resource_mgr_create_string_from_lwcstring(&rm,
+ node->name, &node_name);
+ if (err != DOM_NO_ERR) {
+ return err;
+ }
}
*result = node_name;
@@ -384,10 +505,6 @@
dom_exception _dom_node_get_node_value(dom_node_internal *node,
struct dom_string **result)
{
- if (node->type == DOM_ATTRIBUTE_NODE) {
- return dom_attr_get_value((struct dom_attr *) node, result);
- }
-
if (node->value != NULL)
dom_string_ref(node->value);
@@ -412,6 +529,9 @@
dom_exception _dom_node_set_node_value(dom_node_internal *node,
struct dom_string *value)
{
+ /* TODO
+ * Whether we should change this to a virtual function?
+ */
/* This is a NOP if the value is defined to be null. */
if (node->type == DOM_DOCUMENT_NODE ||
node->type == DOM_DOCUMENT_FRAGMENT_NODE ||
@@ -507,7 +627,7 @@
if (node->owner == NULL)
return DOM_NOT_SUPPORTED_ERR;
- return dom_document_get_nodelist(node->owner, node,
+ return dom_document_get_nodelist(node->owner, DOM_NODELIST_CHILDREN, node,
NULL, NULL, NULL, result);
}
@@ -630,13 +750,10 @@
dom_exception _dom_node_get_attributes(dom_node_internal *node,
struct dom_namednodemap **result)
{
- if (node->type != DOM_ELEMENT_NODE) {
- *result = NULL;
+ UNUSED(node);
+ *result = NULL;
- return DOM_NO_ERR;
- }
-
- return dom_element_get_attributes((struct dom_element *) node, result);
+ return DOM_NO_ERR;
}
/**
@@ -664,7 +781,7 @@
/* If there is an owner, increase its reference count */
if (node->owner != NULL)
- dom_node_ref((dom_node_internal *) node->owner);
+ dom_node_ref(node->owner);
*result = node->owner;
@@ -729,19 +846,10 @@
}
/* Ensure that new_child is permitted as a child of node */
- if (!_dom_node_permitted_child(node, new_child))
+ if (new_child->type != DOM_DOCUMENT_FRAGMENT_NODE &&
+ !_dom_node_permitted_child(node, new_child))
return DOM_HIERARCHY_REQUEST_ERR;
- /* DocumentType nodes are created outside the Document so,
- * if we're trying to attach a DocumentType node, then we
- * also need to set its owner. */
- if (node->type == DOM_DOCUMENT_NODE &&
- new_child->type == DOM_DOCUMENT_TYPE_NODE) {
- /* See long comment in dom_node_initialise as to why
- * we don't ref the document here */
- new_child->owner = (struct dom_document *) node;
- }
-
/* Attempting to insert a node before itself is a NOP */
if (new_child == ref_child) {
dom_node_ref(new_child);
@@ -759,9 +867,18 @@
_dom_node_detach(new_child);
}
+ /* When a Node is attached, it should be removed from the pending list */
+ dom_node_remove_pending(new_child);
+
/* If new_child is a DocumentFragment, insert its children
* Otherwise, insert new_child */
if (new_child->type == DOM_DOCUMENT_FRAGMENT_NODE) {
+ /* Test the children of the docment fragment can be appended */
+ dom_node_internal *c = new_child->first_child;
+ for (; c != NULL; c = c->next)
+ if (!_dom_node_permitted_child(node, c))
+ return DOM_HIERARCHY_REQUEST_ERR;
+
if (new_child->first_child != NULL) {
_dom_node_attach_range(new_child->first_child,
new_child->last_child,
@@ -783,6 +900,16 @@
: ref_child);
}
+ /* DocumentType nodes are created outside the Document so,
+ * if we're trying to attach a DocumentType node, then we
+ * also need to set its owner. */
+ if (node->type == DOM_DOCUMENT_NODE &&
+ new_child->type == DOM_DOCUMENT_TYPE_NODE) {
+ /* See long comment in dom_node_initialise as to why
+ * we don't ref the document here */
+ new_child->owner = (struct dom_document *) node;
+ }
+
/** \todo Is it correct to return DocumentFragments? */
dom_node_ref(new_child);
@@ -853,9 +980,22 @@
}
/* Ensure that new_child is permitted as a child of node */
- if (!_dom_node_permitted_child(node, new_child))
- return DOM_HIERARCHY_REQUEST_ERR;
+ if (new_child->type == DOM_DOCUMENT_FRAGMENT_NODE) {
+ /* If this node is a doc fragment, we should test all its
+ * children nodes */
+ dom_node_internal *c;
+ c = new_child->first_child;
+ while (c != NULL) {
+ if (!_dom_node_permitted_child(node, c))
+ return DOM_HIERARCHY_REQUEST_ERR;
+ c = c->next;
+ }
+ } else {
+ if (!_dom_node_permitted_child(node, new_child))
+ return DOM_HIERARCHY_REQUEST_ERR;
+ }
+
/* Attempting to replace a node with itself is a NOP */
if (new_child == old_child) {
dom_node_ref(old_child);
@@ -878,6 +1018,8 @@
/* Sort out the return value */
dom_node_ref(old_child);
+ /* The replaced node should be marded pending */
+ dom_node_mark_pending(old_child);
*result = old_child;
return DOM_NO_ERR;
@@ -922,8 +1064,13 @@
/* Detach the node */
_dom_node_detach(old_child);
- /* Sort out the return value */
+ /* When a Node is removed, it should be destroy. When its refcnt is not
+ * zero, it will be added to the document's deletion pending list.
+ * When a Node is removed, its parent should be NULL, but its owner should
+ * remain to be the document.
+ */
dom_node_ref(old_child);
+ dom_node_try_destroy(old_child);
*result = old_child;
return DOM_NO_ERR;
@@ -1015,15 +1162,66 @@
*
* \todo work out what happens when cloning Document, DocumentType, Entity
* and Notation nodes.
+ *
+ * Note: we adopt a OO paradigm, this clone_node just provide a basic operation
+ * of clone. Special clones like Attr/EntitiReference stated above should provide
+ * their overload of this interface in their implementation file.
*/
dom_exception _dom_node_clone_node(dom_node_internal *node, bool deep,
dom_node_internal **result)
{
- UNUSED(node);
- UNUSED(deep);
- UNUSED(result);
+ dom_node_internal *n, *child, *r;
+ dom_exception err;
+ dom_document *doc;
+ dom_user_data *ud;
- return DOM_NOT_SUPPORTED_ERR;
+ doc = node->owner;
+ assert(doc != NULL);
+
+ err = dom_node_alloc(doc, node, &n);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ err = dom_node_copy(n, node);
+ if (err != DOM_NO_ERR) {
+ dom_document_alloc(doc, n, 0);
+ return err;
+ }
+
+ if (deep) {
+ child = node->first_child;
+ while (child != NULL) {
+ err = dom_node_clone_node(child, deep, (void *) &r);
+ if (err != DOM_NO_ERR) {
+ dom_node_unref(n);
+ return err;
+ }
+
+ err = dom_node_append_child(n, r, (void *) &r);
+ if (err != DOM_NO_ERR) {
+ dom_node_unref(n);
+ return err;
+ }
+
+ /* Clean up the new node, we have reference it two times */
+ dom_node_unref(r);
+ dom_node_unref(r);
+ child = child->next;
+ }
+ }
+
+ *result = n;
+
+ /* Call the dom_user_data_handlers */
+ ud = node->user_data;
+ while (ud != NULL) {
+ if (ud->handler != NULL)
+ ud->handler(DOM_NODE_CLONED, ud->key, ud->data,
+ (dom_node *) node, (dom_node *) n);
+ ud = ud->next;
+ }
+
+ return DOM_NO_ERR;
}
/**
@@ -1038,9 +1236,36 @@
*/
dom_exception _dom_node_normalize(dom_node_internal *node)
{
- UNUSED(node);
+ dom_node_internal *n, *p;
+ dom_exception err;
- return DOM_NOT_SUPPORTED_ERR;
+ p = node->first_child;
+ if (p == NULL)
+ return DOM_NO_ERR;
+
+ n = p->next;
+
+ while (n != NULL) {
+ if (n->type == DOM_TEXT_NODE && p->type == DOM_TEXT_NODE) {
+ err = merge_adjacent_text(p, n);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ _dom_node_detach(n);
+ dom_node_unref(n);
+ n = p->next;
+ continue;
+ }
+ if (n->type != DOM_TEXT_NODE) {
+ err = dom_node_normalize(n);
+ if (err != DOM_NO_ERR)
+ return err;
+ }
+ p = n;
+ n = n->next;
+ }
+
+ return DOM_NO_ERR;
}
/**
@@ -1054,15 +1279,22 @@
* \return DOM_NO_ERR.
*/
dom_exception _dom_node_is_supported(dom_node_internal *node,
- struct dom_string *feature, dom_node_internal *version,
+ struct dom_string *feature, struct dom_string *version,
bool *result)
{
- UNUSED(node);
- UNUSED(feature);
- UNUSED(version);
- UNUSED(result);
+ dom_document *doc;
+ dom_implementation *impl;
+ bool has;
- return DOM_NOT_SUPPORTED_ERR;
+ doc = node->owner;
+ assert(doc != NULL);
+ dom_document_get_implementation(doc, &impl);
+ assert(impl != NULL);
+ dom_implementation_has_feature(impl, feature, version, &has);
+
+ *result = has;
+
+ return DOM_NO_ERR;
}
/**
@@ -1079,13 +1311,18 @@
dom_exception _dom_node_get_namespace(dom_node_internal *node,
struct dom_string **result)
{
+ lwc_context *ctx;
+
+ assert(node->owner != NULL);
+ ctx = dom_document_get_intern_context(node->owner);
+ assert(ctx != NULL);
+
/* If there is a namespace, increase its reference count */
if (node->namespace != NULL)
- dom_string_ref(node->namespace);
+ lwc_context_string_ref(ctx, node->namespace);
- *result = node->namespace;
-
- return DOM_NO_ERR;
+ return dom_document_create_string_from_lwcstring(node->owner, node->namespace,
+ result);
}
/**
@@ -1102,13 +1339,18 @@
dom_exception _dom_node_get_prefix(dom_node_internal *node,
struct dom_string **result)
{
+ lwc_context *ctx;
+
+ assert(node->owner != NULL);
+ ctx = dom_document_get_intern_context(node->owner);
+ assert(ctx != NULL);
+
/* If there is a prefix, increase its reference count */
if (node->prefix != NULL)
- dom_string_ref(node->prefix);
+ lwc_context_string_ref(ctx, node->prefix);
- *result = node->prefix;
-
- return DOM_NO_ERR;
+ return dom_document_create_string_from_lwcstring(node->owner, node->prefix,
+ result);
}
/**
@@ -1137,6 +1379,13 @@
dom_exception _dom_node_set_prefix(dom_node_internal *node,
struct dom_string *prefix)
{
+ dom_exception err;
+ lwc_string *str;
+ lwc_context *docctx;
+
+ docctx = dom_document_get_intern_context(node->owner);
+ assert(docctx != NULL);
+
/* Only Element and Attribute nodes created using
* namespace-aware methods may have a prefix */
if ((node->type != DOM_ELEMENT_NODE &&
@@ -1154,7 +1403,7 @@
/* No longer want existing prefix */
if (node->prefix != NULL) {
- dom_string_unref(node->prefix);
+ lwc_context_string_unref(docctx, node->prefix);
}
/* Set the prefix */
@@ -1163,8 +1412,11 @@
if (dom_string_length(prefix) == 0) {
node->prefix = NULL;
} else {
- dom_string_ref(prefix);
- node->prefix = prefix;
+ err = dom_node_get_intern_string(node, prefix, &str);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ node->prefix = str;
}
} else {
node->prefix = NULL;
@@ -1186,7 +1438,13 @@
*/
dom_exception _dom_node_get_local_name(dom_node_internal *node,
struct dom_string **result)
-{
+{
+ lwc_context *ctx;
+
+ assert(node->owner != NULL);
+ ctx = dom_document_get_intern_context(node->owner);
+ assert(ctx != NULL);
+
/* Only Element and Attribute nodes may have a local name */
if (node->type != DOM_ELEMENT_NODE &&
node->type != DOM_ATTRIBUTE_NODE) {
@@ -1194,19 +1452,13 @@
return DOM_NO_ERR;
}
- /* Node must have been created using a namespace-aware method */
- if (node->namespace == NULL) {
- *result = NULL;
- return DOM_NO_ERR;
- }
-
/* The node may have a local name, reference it if so */
if (node->name != NULL) {
- dom_string_ref(node->name);
+ lwc_context_string_ref(ctx, node->name);
}
- *result = node->name;
- return DOM_NO_ERR;
+ return dom_document_create_string_from_lwcstring(node->owner, node->name,
+ result);
}
/**
@@ -1218,13 +1470,10 @@
*/
dom_exception _dom_node_has_attributes(dom_node_internal *node, bool *result)
{
- if (node->type != DOM_ELEMENT_NODE) {
- *result = false;
+ UNUSED(node);
+ *result = false;
- return DOM_NO_ERR;
- }
-
- return dom_element_has_attributes((struct dom_element *) node, result);
+ return DOM_NO_ERR;
}
/**
@@ -1286,10 +1535,21 @@
dom_exception _dom_node_get_text_content(dom_node_internal *node,
struct dom_string **result)
{
- UNUSED(node);
- UNUSED(result);
+ dom_node_internal *n;
+ dom_string *str;
+ dom_string *ret;
- return DOM_NOT_SUPPORTED_ERR;
+ assert(node->owner != NULL);
+
+ for (n = node->first_child; n != NULL; n = n->next) {
+ dom_node_get_text_content(n, &ret);
+ dom_string_concat(str, ret, &str);
+ }
+
+ dom_string_ref(str);
+ *result = str;
+
+ return DOM_NO_ERR;
}
/**
@@ -1306,10 +1566,36 @@
dom_exception _dom_node_set_text_content(dom_node_internal *node,
struct dom_string *content)
{
- UNUSED(node);
- UNUSED(content);
+ dom_node_internal *n, *p, *r;
+ dom_document *doc;
+ dom_text *text;
+ dom_exception err;
- return DOM_NOT_SUPPORTED_ERR;
+ n = node->first_child;
+
+ while (n != NULL) {
+ p = n;
+ n = n->next;
+ /* Add the (void *) casting to avoid gcc warning:
+ * dereferencing type-punned pointer will break
+ * strict-aliasing rules */
+ err = dom_node_remove_child(node, n, (void *) &r);
+ if (err != DOM_NO_ERR)
+ return err;
+ }
+
+ doc = node->owner;
+ assert(doc != NULL);
+
+ err = dom_document_create_text_node(doc, content, &text);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ err = dom_node_append_child(node, text, (void *) &r);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ return DOM_NO_ERR;
}
/**
@@ -1345,11 +1631,12 @@
dom_exception _dom_node_lookup_prefix(dom_node_internal *node,
struct dom_string *namespace, struct dom_string **result)
{
- UNUSED(node);
- UNUSED(namespace);
- UNUSED(result);
+ if (node->parent != NULL)
+ return dom_node_lookup_prefix(node, namespace, result);
+ else
+ *result = NULL;
- return DOM_NOT_SUPPORTED_ERR;
+ return DOM_NO_ERR;
}
/**
@@ -1363,11 +1650,11 @@
dom_exception _dom_node_is_default_namespace(dom_node_internal *node,
struct dom_string *namespace, bool *result)
{
- UNUSED(node);
- UNUSED(namespace);
- UNUSED(result);
-
- return DOM_NOT_SUPPORTED_ERR;
+ if (node->parent != NULL)
+ return dom_node_is_default_namespace(node, namespace, result);
+ else
+ *result = false;
+ return DOM_NO_ERR;
}
/**
@@ -1385,11 +1672,12 @@
dom_exception _dom_node_lookup_namespace(dom_node_internal *node,
struct dom_string *prefix, struct dom_string **result)
{
- UNUSED(node);
- UNUSED(prefix);
- UNUSED(result);
+ if (node->parent != NULL)
+ return dom_node_lookup_namespace(node->parent, prefix, result);
+ else
+ *result = NULL;
- return DOM_NOT_SUPPORTED_ERR;
+ return DOM_NO_ERR;
}
/**
@@ -1410,15 +1698,104 @@
* + publicId, systemId, internalSubset are equal
* + The node entities are equal
* + The node notations are equal
+ * TODO: in document_type, we should override this virtual function
*/
dom_exception _dom_node_is_equal(dom_node_internal *node,
dom_node_internal *other, bool *result)
{
- UNUSED(node);
- UNUSED(other);
- UNUSED(result);
+ dom_exception err;
+ dom_string *s1, *s2;
+ lwc_context *c1, *c2;
+ dom_namednodemap *m1, *m2;
+ dom_nodelist *l1, *l2;
- return DOM_NOT_SUPPORTED_ERR;
+ if (node->type != other->type){
+ *result = false;
+ return DOM_NO_ERR;
+ }
+
+ assert(node->owner != NULL);
+ assert(other->owner != NULL);
+
+ err = dom_node_get_node_name(node, &s1);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ err = dom_node_get_node_name(other, &s2);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ if (dom_string_cmp(s1, s2) != 0) {
+ *result = false;
+ return DOM_NO_ERR;
+ }
+
+ c1 = dom_document_get_intern_context(node->owner);
+ assert(c1 != NULL);
+
+ c2 = dom_document_get_intern_context(other->owner);
+ assert(c2 != NULL);
+
+ if (c1 == c2) {
+ if (node->name != other->name ||
+ node->namespace != other->namespace ||
+ node->prefix != other->prefix) {
+ *result = false;
+ return DOM_NO_ERR;
+ }
+ } else {
+ if (dom_lwc_string_compare_raw(node->name, other->name) != 0){
+ *result = false;
+ return DOM_NO_ERR;
+ }
+
+ if (dom_lwc_string_compare_raw(node->namespace, other->namespace) != 0){
+ *result = false;
+ return DOM_NO_ERR;
+ }
+
+ if (dom_lwc_string_compare_raw(node->prefix, other->prefix) != 0){
+ *result = false;
+ return DOM_NO_ERR;
+ }
+ }
+
+ if (dom_string_cmp(node->value, other->value) != 0) {
+ *result = false;
+ return DOM_NO_ERR;
+ }
+
+ // Following comes the attributes
+ err = dom_node_get_attributes(node, &m1);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ err = dom_node_get_attributes(other, &m2);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ if (dom_namednodemap_equal(m1, m2) != true) {
+ *result = false;
+ return DOM_NO_ERR;
+ }
+
+ // Finally the childNodes
+ err = dom_node_get_child_nodes(node, &l1);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ err = dom_node_get_child_nodes(other, &l2);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ if (dom_nodelist_equal(l1, l2) != true) {
+ *result = false;
+ return DOM_NO_ERR;
+ }
+
+ *result = true;
+
+ return DOM_NO_ERR;
}
/**
@@ -1435,12 +1812,23 @@
struct dom_string *feature, struct dom_string *version,
void **result)
{
- UNUSED(node);
- UNUSED(feature);
- UNUSED(version);
- UNUSED(result);
+ dom_document *doc;
+ dom_implementation *impl;
+ bool has;
- return DOM_NOT_SUPPORTED_ERR;
+ doc = node->owner;
+ assert(doc != NULL);
+ dom_document_get_implementation(doc, &impl);
+ assert(impl != NULL);
+ dom_implementation_has_feature(impl, feature, version, &has);
+
+ if (has) {
+ *result = node;
+ } else {
+ *result = NULL;
+ }
+
+ return DOM_NO_ERR;
}
/**
@@ -1545,7 +1933,124 @@
/* */
/*----------------------------------------------------------------------------*/
/* */
+/* The protected virtual functions */
+/* We should never call this pure-virtual function directly */
+dom_exception _dom_node_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret)
+{
+ UNUSED(doc);
+ UNUSED(n);
+ UNUSED(ret);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+
+/*
+ * Copy the internal attributes of a Node from old to new
+ */
+dom_exception _dom_node_copy(dom_node_internal *new, dom_node_internal *old)
+{
+ lwc_context *nctx, *octx;
+ dom_exception err;
+
+ new->vtable = old->vtable;
+ new->base.vtable = old->base.vtable;
+
+ assert(old->owner != NULL);
+ octx = dom_document_get_intern_context(old->owner);
+ assert(octx != NULL);
+
+ assert(new->owner != NULL);
+ nctx = dom_document_get_intern_context(old->owner);
+ assert(nctx != NULL);
+
+ new->type = old->type;
+ new->parent = NULL;
+ new->first_child = NULL;
+ new->last_child = NULL;
+ new->previous = NULL;
+ new->next = NULL;
+ new->owner = old->owner;
+
+ if (octx == nctx) {
+ lwc_context_string_ref(octx, old->name);
+ new->name = old->name;
+
+ if (old->namespace != NULL)
+ lwc_context_string_ref(octx, old->namespace);
+ new->namespace = old->namespace;
+
+ if (old->prefix != NULL)
+ lwc_context_string_ref(octx, old->prefix);
+ new->prefix = old->prefix;
+ } else {
+ lwc_string *str;
+ lwc_error lerr;
+
+ lerr = lwc_context_intern(nctx, lwc_string_data(old->name),
+ lwc_string_length(old->name), &str);
+ if (lerr != lwc_error_ok)
+ return dom_exception_from_lwc_error(lerr);
+
+ new->name = str;
+
+ if (old->namespace != NULL) {
+ lerr = lwc_context_intern(nctx,
+ lwc_string_data(old->namespace),
+ lwc_string_length(old->namespace), &str);
+ if (lerr != lwc_error_ok)
+ return dom_exception_from_lwc_error(lerr);
+
+ new->namespace = str;
+ } else
+ new->namespace = NULL;
+
+ if (old->prefix != NULL) {
+ lerr = lwc_context_intern(nctx,
+ lwc_string_data(old->prefix),
+ lwc_string_length(old->prefix), &str);
+ if (lerr != lwc_error_ok)
+ return dom_exception_from_lwc_error(lerr);
+
+ new->prefix = str;
+ } else
+ new->prefix = NULL;
+ }
+
+ dom_alloc al;
+ void *pw;
+
+ if (old->value != NULL) {
+ dom_document_get_allocator(new->owner, &al, &pw);
+ dom_string *value;
+ err = dom_string_clone(al, pw, old->value, &value);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ new->value = value;
+ } else {
+ new->value = NULL;
+ }
+
+ new->user_data = NULL;
+ new->refcnt = 1;
+
+ list_init(&new->pending_list);
+ /* The new copyed node has no parent,
+ * so it should be put in the pending list.
+ */
+ dom_node_mark_pending(new);
+
+ return DOM_NO_ERR;
+}
+
+/* */
+/*----------------------------------------------------------------------------*/
+/* */
+/* The helper functions */
+
/**
* Determine if a node is permitted as a child of another node
*
@@ -1626,23 +2131,21 @@
*/
bool _dom_node_readonly(const dom_node_internal *node)
{
- /* DocumentType and Notation nodes are read only */
- if (node->type == DOM_DOCUMENT_TYPE_NODE ||
- node->type == DOM_NOTATION_NODE)
+ const dom_node_internal *n = node;
+
+ /* DocumentType and Notation ns are read only */
+ if (n->type == DOM_DOCUMENT_TYPE_NODE ||
+ n->type == DOM_NOTATION_NODE)
return true;
- /* Entity nodes and their descendants are read only */
- for (; node != NULL; node = node->parent) {
- if (node->type == DOM_ENTITY_NODE)
+ /* Entity ns and their descendants are read only
+ * EntityReference ns and their descendants are read only */
+ for (n = node; n != NULL; n = n->parent) {
+ if (n->type == DOM_ENTITY_NODE
+ || n->type == DOM_ENTITY_REFERENCE_NODE)
return true;
}
- /* EntityReference nodes and their descendants are read only */
- for (; node != NULL; node = node->parent) {
- if (node->type == DOM_ENTITY_REFERENCE_NODE)
- return true;
- }
-
/* Otherwise, it's writable */
return false;
}
@@ -1668,6 +2171,10 @@
*/
void _dom_node_detach(dom_node_internal *node)
{
+ /* When a Node is not in the document tree, it must be in the
+ pending list */
+ dom_node_mark_pending(node);
+
_dom_node_detach_range(node, node);
}
@@ -1701,8 +2208,9 @@
else
parent->last_child = last;
- for (dom_node_internal *n = first; n != last->next; n = n->next)
+ for (dom_node_internal *n = first; n != last->next; n = n->next) {
n->parent = parent;
+ }
}
/**
@@ -1726,8 +2234,9 @@
else
last->parent->last_child = first->previous;
- for (dom_node_internal *n = first; n != last->next; n = n->next)
+ for (dom_node_internal *n = first; n != last->next; n = n->next) {
n->parent = NULL;
+ }
first->previous = NULL;
last->next = NULL;
@@ -1771,9 +2280,205 @@
else
old->parent->last_child = last;
- for (dom_node_internal *n = first; n != last->next; n = n->next)
+ for (dom_node_internal *n = first; n != last->next; n = n->next) {
n->parent = old->parent;
+ }
old->previous = old->next = old->parent = NULL;
}
+/**
+ * Migrate one lwc_string from one context to another, this function
+ * may be used when we import/adopt a Node between documents.
+ *
+ * \param old The source context
+ * \param new The new context
+ * \param string The lwc_string to migrate
+ */
+dom_exception _redocument_lwcstring(lwc_context *old, lwc_context *new,
+ lwc_string **string)
+{
+ lwc_string *str;
+ lwc_error lerr;
+
+ lerr = lwc_context_intern(new, lwc_string_data(*string),
+ lwc_string_length(*string), &str);
+ if (lerr != lwc_error_ok)
+ return dom_exception_from_lwc_error(lerr);
+
+ lwc_context_string_unref(old, *string);
+ *string = str;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Migrate one dom_string from one document to another, this function
+ * may be used when we import/adopt a Node between documents.
+ *
+ * \param old The source document
+ * \param new The new document
+ * \param string The dom_string to migrate
+ */
+dom_exception _redocument_domstring(dom_document *old, dom_document* new,
+ dom_string **string)
+{
+ dom_exception err;
+ dom_string *str;
+
+ UNUSED(old);
+ err = dom_document_create_string(new, NULL, 0, &str);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ err = dom_string_dup(*string, &str);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ dom_string_unref(*string);
+ *string = str;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Merge two adjacent text into one text node.
+ *
+ * \param p The first text node
+ * \param n The second text node
+ */
+dom_exception merge_adjacent_text(dom_node_internal *p, dom_node_internal *n)
+{
+ assert(p->type = DOM_TEXT_NODE);
+ assert(n->type = DOM_TEXT_NODE);
+
+ dom_string *str;
+ dom_exception err;
+
+ err = dom_text_get_whole_text(n, &str);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ err = dom_characterdata_append_data(p, str);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ dom_string_unref(str);
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Intern a dom_string using the node's owner document's lwc_context
+ *
+ * \param node The node
+ * \param str The dom_string to be interned
+ * \param intern The returned interned string
+ */
+dom_exception dom_node_get_intern_string(dom_node_internal *node,
+ dom_string *str, lwc_string **intern)
+{
+ dom_exception err;
+ lwc_context *ctx, *docctx;
+ lwc_string *ret;
+
+ assert(str != NULL);
+ assert(node->owner != NULL);
+
+ docctx = dom_document_get_intern_context(node->owner);
+ assert(docctx != NULL);
+
+ err = dom_string_get_intern(str, &ctx, &ret);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ if (ctx != docctx) {
+ err = dom_string_intern(str, docctx, &ret);
+ if (err != DOM_NO_ERR)
+ return err;
+ }
+
+ *intern = ret;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Unref a lwc_string used in this node
+ *
+ * \param node The node
+ * \param intern The lwc_string to unref
+ */
+void dom_node_unref_intern_string(dom_node_internal *node,
+ struct lwc_string_s *intern)
+{
+ struct resource_mgr rm;
+ struct dom_document *doc = node->owner;
+
+ if (doc != NULL) {
+ dom_document_get_resource_mgr(doc, &rm);
+ } else if (node->type == DOM_DOCUMENT_TYPE_NODE) {
+ dom_document_type_get_resource_mgr((dom_document_type *) node, &rm);
+ }
+
+ lwc_context_string_unref(rm.ctx, intern);
+}
+
+/**
+ * Try to destroy this node.
+ * When some node owns this node, (such as an elment owns its attribute nodes)
+ * when this node being not owned, the owner should call this function to try
+ * to destroy this node.
+ *
+ * @note: Owning a node does not means this node's refcnt is above zero.
+ *
+ * \param node The node to destroy
+ */
+void _dom_node_try_destroy(dom_node_internal *node)
+{
+ if (node == NULL)
+ return;
+
+ if (node->parent == NULL) {
+ if (node->refcnt == 0) {
+ dom_node_destroy(node);
+ } else if (node->pending_list.prev == &node->pending_list){
+ assert (node->pending_list.next == &node->pending_list);
+ list_append(&node->owner->pending_nodes, &node->pending_list);
+ }
+ }
+}
+
+/* To add some node to the pending list, when a node is removed from its parent
+ * or an attribute is removed from its element
+*/
+void _dom_node_mark_pending(dom_node_internal *node)
+{
+ struct dom_document *doc = node->owner;
+
+ /* TODO: the pending_list is located at in dom_document, but some
+ * nodes can be created without a document created, such as a
+ * dom_document_type node. For this reason, we should test whether
+ * the doc is NULL
+ */
+ if (doc != NULL) {
+ /* The node must not be in the pending list */
+ assert(node->pending_list.prev == &node->pending_list);
+
+ list_append(&doc->pending_nodes, &node->pending_list);
+ }
+}
+
+/* To remove the node from the pending list, this may happen when
+ * a node is removed and then appended to another parent */
+void _dom_node_remove_pending(dom_node_internal *node)
+{
+ struct dom_document *doc = node->owner;
+
+ if (doc != NULL) {
+ /* The node must be in the pending list */
+ assert(node->pending_list.prev != &node->pending_list);
+
+ list_del(&node->pending_list);
+ }
+}
Index: src/core/namednodemap.c
===================================================================
--- src/core/namednodemap.c (revision 8909)
+++ src/core/namednodemap.c (working copy)
@@ -3,8 +3,11 @@
* Licensed under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
* Copyright 2007 John-Mark Bell <jmb(a)netsurf-browser.org>
+ * Copyright 2009 Bo Yang <struggleyb.nku(a)gmail.com>
*/
+#include <assert.h>
+
#include <dom/core/element.h>
#include <dom/core/node.h>
#include <dom/core/string.h>
@@ -22,9 +25,10 @@
struct dom_namednodemap {
struct dom_document *owner; /**< Owning document */
- struct dom_node_internal *head; /**< Start of item list */
+ void *priv; /**< Private data */
- dom_node_type type; /**< Type of items in map */
+ struct nnm_operation *opt; /**< The underlaid operation
+ * implementations */
uint32_t refcnt; /**< Reference count */
};
@@ -33,7 +37,7 @@
* Create a namednodemap
*
* \param doc The owning document
- * \param head Start of list containing items in map
+ * \param Start of list containing items in map
* \param type The type of items in the map
* \param map Pointer to location to receive created map
* \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion
@@ -50,7 +54,7 @@
* finished with it.
*/
dom_exception dom_namednodemap_create(struct dom_document *doc,
- struct dom_node_internal *head, dom_node_type type,
+ void *priv, struct nnm_operation *opt,
struct dom_namednodemap **map)
{
struct dom_namednodemap *m;
@@ -59,14 +63,11 @@
if (m == NULL)
return DOM_NO_MEM_ERR;
- dom_node_ref((struct dom_node *) doc);
m->owner = doc;
- dom_node_ref(head);
- m->head = head;
+ m->priv = priv;
+ m->opt = opt;
- m->type = type;
-
m->refcnt = 1;
*map = m;
@@ -81,6 +82,7 @@
*/
void dom_namednodemap_ref(struct dom_namednodemap *map)
{
+ assert(map != NULL);
map->refcnt++;
}
@@ -94,22 +96,15 @@
*/
void dom_namednodemap_unref(struct dom_namednodemap *map)
{
+ if (map == NULL)
+ return;
+
if (--map->refcnt == 0) {
- struct dom_node_internal *owner =
- (struct dom_node_internal *) map->owner;
+ /* Call the implementation specific destroy */
+ map->opt->namednodemap_destroy(map->priv);
- dom_node_unref(map->head);
-
- /* Remove map from document */
- dom_document_remove_namednodemap(map->owner, map);
-
/* Destroy the map object */
dom_document_alloc(map->owner, map, 0);
-
- /* And release our reference on the owning document
- * This must be last as, otherwise, it's possible that
- * the document is destroyed before we are */
- dom_node_unref(owner);
}
}
@@ -123,29 +118,8 @@
dom_exception dom_namednodemap_get_length(struct dom_namednodemap *map,
unsigned long *length)
{
- struct dom_node_internal *cur;
- unsigned long len = 0;
-
- switch (map->type) {
- case DOM_ATTRIBUTE_NODE:
- cur = dom_element_get_first_attribute(
- (struct dom_element *) map->head);
- break;
- case DOM_NOTATION_NODE:
- case DOM_ENTITY_NODE:
- /** \todo handle notation and entity nodes */
- default:
- return DOM_NOT_SUPPORTED_ERR;
- break;
- }
-
- for (; cur != NULL; cur = cur->next) {
- len++;
- }
-
- *length = len;
-
- return DOM_NO_ERR;
+ assert(map->opt != NULL);
+ return map->opt->namednodemap_get_length(map->priv, length);
}
/**
@@ -162,33 +136,8 @@
dom_exception _dom_namednodemap_get_named_item(struct dom_namednodemap *map,
struct dom_string *name, struct dom_node **node)
{
- struct dom_node_internal *cur;
-
- switch (map->type) {
- case DOM_ATTRIBUTE_NODE:
- cur = dom_element_get_first_attribute(
- (struct dom_element *) map->head);
- break;
- case DOM_NOTATION_NODE:
- case DOM_ENTITY_NODE:
- /** \todo handle notation and entity nodes */
- default:
- return DOM_NOT_SUPPORTED_ERR;
- break;
- }
-
- for (; cur != NULL; cur = cur->next) {
- if (dom_string_cmp(cur->name, name) == 0) {
- break;
- }
- }
-
- if (cur != NULL) {
- dom_node_ref(cur);
- }
- *node = (struct dom_node *) cur;
-
- return DOM_NO_ERR;
+ assert(map->opt != NULL);
+ return map->opt->namednodemap_get_named_item(map->priv, name, node);
}
/**
@@ -218,46 +167,8 @@
dom_exception _dom_namednodemap_set_named_item(struct dom_namednodemap *map,
struct dom_node *arg, struct dom_node **node)
{
- dom_exception err;
- struct dom_node_internal *n = (struct dom_node_internal *) arg;
-
- /* Ensure arg and map belong to the same document */
- if (n->owner != map->owner)
- return DOM_WRONG_DOCUMENT_ERR;
-
- /* Ensure map is writable */
- if (_dom_node_readonly(map->head))
- return DOM_NO_MODIFICATION_ALLOWED_ERR;
-
- /* Ensure arg isn't attached to another element */
- if (n->type == DOM_ATTRIBUTE_NODE && n->parent != NULL &&
- n->parent != map->head)
- return DOM_INUSE_ATTRIBUTE_ERR;
-
- /* Ensure arg is permitted in the map */
- if (n->type != map->type)
- return DOM_HIERARCHY_REQUEST_ERR;
-
- /* Now delegate to the container-specific function.
- * NamedNodeMaps are live, so this is fine. */
- switch (map->type) {
- case DOM_ATTRIBUTE_NODE:
- err = dom_element_set_attribute_node(
- (struct dom_element *) map->head,
- (struct dom_attr *) arg,
- (struct dom_attr **) node);
- break;
- case DOM_NOTATION_NODE:
- case DOM_ENTITY_NODE:
- /** \todo handle notation and entity nodes */
- default:
- err = DOM_NOT_SUPPORTED_ERR;
- break;
- }
-
- /* Reference counting is handled by the container-specific call */
-
- return err;
+ assert(map->opt != NULL);
+ return map->opt->namednodemap_set_named_item(map->priv, arg, node);
}
/**
@@ -278,44 +189,8 @@
struct dom_namednodemap *map, struct dom_string *name,
struct dom_node **node)
{
- dom_exception err;
-
- /* Ensure map is writable */
- if (_dom_node_readonly(map->head))
- return DOM_NO_MODIFICATION_ALLOWED_ERR;
-
- /* Now delegate to the container-specific function.
- * NamedNodeMaps are live, so this is fine. */
- switch (map->type) {
- case DOM_ATTRIBUTE_NODE:
- {
- struct dom_attr *attr;
-
- err = dom_element_get_attribute_node(
- (struct dom_element *) map->head,
- name, &attr);
- if (err == DOM_NO_ERR) {
- err = dom_element_remove_attribute_node(
- (struct dom_element *) map->head,
- attr, (struct dom_attr **) node);
- if (err == DOM_NO_ERR) {
- /* No longer want attr */
- dom_node_unref((struct dom_node *) attr);
- }
- }
- }
- break;
- case DOM_NOTATION_NODE:
- case DOM_ENTITY_NODE:
- /** \todo handle notation and entity nodes */
- default:
- err = DOM_NOT_SUPPORTED_ERR;
- break;
- }
-
- /* Reference counting is handled by the container-specific call */
-
- return err;
+ assert(map->opt != NULL);
+ return map->opt->namednodemap_remove_named_item(map->priv, name, node);
}
/**
@@ -335,36 +210,8 @@
dom_exception _dom_namednodemap_item(struct dom_namednodemap *map,
unsigned long index, struct dom_node **node)
{
- struct dom_node_internal *cur;
- unsigned long count = 0;
-
- switch (map->type) {
- case DOM_ATTRIBUTE_NODE:
- cur = dom_element_get_first_attribute(
- (struct dom_element *) map->head);
- break;
- case DOM_NOTATION_NODE:
- case DOM_ENTITY_NODE:
- /** \todo handle notation and entity nodes */
- default:
- return DOM_NOT_SUPPORTED_ERR;
- break;
- }
-
- for (; cur != NULL; cur = cur->next) {
- count++;
-
- if ((index + 1) == count) {
- break;
- }
- }
-
- if (cur != NULL) {
- dom_node_ref(cur);
- }
- *node = (struct dom_node *) cur;
-
- return DOM_NO_ERR;
+ assert(map->opt != NULL);
+ return map->opt->namednodemap_item(map->priv, index, node);
}
/**
@@ -387,38 +234,8 @@
struct dom_namednodemap *map, struct dom_string *namespace,
struct dom_string *localname, struct dom_node **node)
{
- struct dom_node_internal *cur;
-
- /** \todo ensure XML feature is supported */
-
- switch (map->type) {
- case DOM_ATTRIBUTE_NODE:
- cur = dom_element_get_first_attribute(
- (struct dom_element *) map->head);
- break;
- case DOM_NOTATION_NODE:
- case DOM_ENTITY_NODE:
- /** \todo handle notation and entity nodes */
- default:
- return DOM_NOT_SUPPORTED_ERR;
- break;
- }
-
- for (; cur != NULL; cur = cur->next) {
- if (((namespace == NULL && cur->namespace == NULL) ||
- (namespace != NULL &&
- dom_string_cmp(cur->namespace, namespace) == 0)) &&
- dom_string_cmp(cur->name, localname) == 0) {
- break;
- }
- }
-
- if (cur != NULL) {
- dom_node_ref(cur);
- }
- *node = (struct dom_node *) cur;
-
- return DOM_NO_ERR;
+ assert(map->opt != NULL);
+ return map->opt->namednodemap_get_named_item_ns(map->priv, namespace, localname, node);
}
/**
@@ -454,48 +271,8 @@
struct dom_namednodemap *map, struct dom_node *arg,
struct dom_node **node)
{
- dom_exception err;
- struct dom_node_internal *n = (struct dom_node_internal *) arg;
-
- /** \todo ensure XML feature is supported */
-
- /* Ensure arg and map belong to the same document */
- if (n->owner != map->owner)
- return DOM_WRONG_DOCUMENT_ERR;
-
- /* Ensure map is writable */
- if (_dom_node_readonly(map->head))
- return DOM_NO_MODIFICATION_ALLOWED_ERR;
-
- /* Ensure arg isn't attached to another element */
- if (n->type == DOM_ATTRIBUTE_NODE && n->parent != NULL &&
- n->parent != map->head)
- return DOM_INUSE_ATTRIBUTE_ERR;
-
- /* Ensure arg is permitted in the map */
- if (n->type != map->type)
- return DOM_HIERARCHY_REQUEST_ERR;
-
- /* Now delegate to the container-specific function.
- * NamedNodeMaps are live, so this is fine. */
- switch (map->type) {
- case DOM_ATTRIBUTE_NODE:
- err = dom_element_set_attribute_node_ns(
- (struct dom_element *) map->head,
- (struct dom_attr *) arg,
- (struct dom_attr **) node);
- break;
- case DOM_NOTATION_NODE:
- case DOM_ENTITY_NODE:
- /** \todo handle notation and entity nodes */
- default:
- err = DOM_NOT_SUPPORTED_ERR;
- break;
- }
-
- /* Reference counting is handled by the container-specific call */
-
- return err;
+ assert(map->opt != NULL);
+ return map->opt->namednodemap_set_named_item_ns(map->priv, arg, node);
}
/**
@@ -521,61 +298,28 @@
struct dom_namednodemap *map, struct dom_string *namespace,
struct dom_string *localname, struct dom_node **node)
{
- dom_exception err;
-
- /** \todo ensure XML feature is supported */
-
- /* Ensure map is writable */
- if (_dom_node_readonly(map->head))
- return DOM_NO_MODIFICATION_ALLOWED_ERR;
-
- /* Now delegate to the container-specific function.
- * NamedNodeMaps are live, so this is fine. */
- switch (map->type) {
- case DOM_ATTRIBUTE_NODE:
- {
- struct dom_attr *attr;
-
- err = dom_element_get_attribute_node_ns(
- (struct dom_element *) map->head,
- namespace, localname, &attr);
- if (err == DOM_NO_ERR) {
- err = dom_element_remove_attribute_node(
- (struct dom_element *) map->head,
- attr, (struct dom_attr **) node);
- if (err == DOM_NO_ERR) {
- /* No longer want attr */
- dom_node_unref((struct dom_node *) attr);
- }
- }
- }
- break;
-case DOM_NOTATION_NODE:
- case DOM_ENTITY_NODE:
- /** \todo handle notation and entity nodes */
- default:
- err = DOM_NOT_SUPPORTED_ERR;
- break;
- }
-
- /* Reference counting is handled by the container-specific call */
-
- return err;
+ assert(map->opt != NULL);
+ return map->opt->namednodemap_remove_named_item_ns(map->priv, namespace, localname, node);
}
/**
- * Match a namednodemap instance against a set of creation parameters
+ * Compare whether two NamedNodeMap are equal.
*
- * \param map The map to match
- * \param head Start of list containing items in map
- * \param type The type of items in the map
- * \return true if list matches, false otherwise
*/
-bool dom_namednodemap_match(struct dom_namednodemap *map,
- struct dom_node_internal *head, dom_node_type type)
+bool _dom_namednodemap_equal(struct dom_namednodemap *m1,
+ struct dom_namednodemap *m2)
{
- if (map->head == head && map->type == type)
- return true;
+ assert(m1->opt != NULL);
+ return (m1->opt == m2->opt && m1->opt->namednodemap_equal(m1->priv, m2->priv));
+}
- return false;
+/**
+ * Update the dom_namednodemap to make it as a proxy of another object
+ *
+ * \param map The dom_namednodemap
+ * \param priv The private data to change to
+ */
+void dom_namednodemap_update(struct dom_namednodemap *map, void *priv)
+{
+ map->priv = priv;
}
Index: src/core/node.h
===================================================================
--- src/core/node.h (revision 8909)
+++ src/core/node.h (working copy)
@@ -10,8 +10,13 @@
#include <stdbool.h>
+#include <libwapcaplet/libwapcaplet.h>
+
#include <dom/core/node.h>
+#include <dom/functypes.h>
+#include "utils/list.h"
+
/**
* User data context attached to a DOM node
*/
@@ -23,7 +28,23 @@
struct dom_user_data *next; /**< Next in list */
struct dom_user_data *prev; /**< Previous in list */
};
+typedef struct dom_user_data dom_user_data;
+/* Internally, we need virtual functions too */
+typedef struct dom_node_protect_vtable {
+
+ void (*destroy)(dom_node_internal *n);
+ /**< The destroy vitual function, it
+ * should be privated to client */
+ dom_exception (*alloc)(struct dom_document *doc,
+ dom_node_internal *n, dom_node_internal **ret);
+ /**< Alloc the memory of the new Node */
+ dom_exception (*copy)(dom_node_internal *new, dom_node_internal *old);
+ /**< Copy the old to new as well as
+ * all its attribute, but not its childs
+ */
+} dom_node_protect_vtable;
+
/**
* The real DOM node object
*
@@ -31,11 +52,9 @@
*/
struct dom_node_internal {
struct dom_node base; /**< The vtable base */
- void (*destroy)(dom_node_internal *n);
- /**< The destroy vitual function, it
- * should be privated to client */
+ void *vtable; /**< The protected vtable */
- struct dom_string *name; /**< Node name (this is the local part
+ struct lwc_string_s *name; /**< Node name (this is the local part
* of a QName in the cases where a
* namespace exists) */
struct dom_string *value; /**< Node value */
@@ -48,27 +67,37 @@
struct dom_document *owner; /**< Owning document */
- struct dom_string *namespace; /**< Namespace URI */
- struct dom_string *prefix; /**< Namespace prefix */
+ struct lwc_string_s *namespace; /**< Namespace URI */
+ struct lwc_string_s *prefix; /**< Namespace prefix */
struct dom_user_data *user_data; /**< User data list */
uint32_t refcnt; /**< Reference count */
+
+ struct list_entry pending_list; /**< The document delete pending list */
};
dom_node_internal * dom_node_create(struct dom_document *doc);
dom_exception dom_node_initialise(struct dom_node_internal *node,
struct dom_document *doc, dom_node_type type,
- struct dom_string *name, struct dom_string *value,
- struct dom_string *namespace, struct dom_string *prefix);
+ struct lwc_string_s *name, struct dom_string *value,
+ struct lwc_string_s *namespace, struct lwc_string_s *prefix);
+dom_exception dom_node_initialise_generic(
+ struct dom_node_internal *node, struct dom_document *doc,
+ dom_alloc alloc, void *pw, struct lwc_context_s *ctx,
+ dom_node_type type, struct lwc_string_s *name,
+ struct dom_string *value, struct lwc_string_s *namespace,
+ struct lwc_string_s *prefix);
+
void dom_node_finalise(struct dom_document *doc, dom_node_internal *node);
+void dom_node_finalise_generic(dom_node_internal *node, dom_alloc alloc,
+ void *pw, struct lwc_context_s *ctx);
bool _dom_node_readonly(const dom_node_internal *node);
/* The DOM Node's vtable methods */
-void _dom_node_destroy(struct dom_node_internal *node);
dom_exception _dom_node_get_node_name(dom_node_internal *node,
struct dom_string **result);
dom_exception _dom_node_get_node_value(dom_node_internal *node,
@@ -110,7 +139,7 @@
dom_node_internal **result);
dom_exception _dom_node_normalize(dom_node_internal *node);
dom_exception _dom_node_is_supported(dom_node_internal *node,
- struct dom_string *feature, dom_node_internal *version,
+ struct dom_string *feature, struct dom_string *version,
bool *result);
dom_exception _dom_node_get_namespace(dom_node_internal *node,
struct dom_string **result);
@@ -188,11 +217,84 @@
_dom_node_get_user_data
+/* Follwoing comes the protected vtable */
+void _dom_node_destroy(struct dom_node_internal *node);
+dom_exception _dom_node_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret);
+dom_exception _dom_node_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old);
+
+#define DOM_NODE_PROTECT_VTABLE \
+ _dom_node_destroy, \
+ _dom_node_alloc, \
+ _dom_node_copy
+
+
/* The destroy API should be used inside DOM module */
-static inline void dom_node_destroy(struct dom_node *node)
+static inline void dom_node_destroy(struct dom_node_internal *node)
{
- ((dom_node_internal *) node)->destroy((dom_node_internal *) node);
+ ((dom_node_protect_vtable *) node->vtable)->destroy(node);
}
-#define dom_node_destroy(n) dom_node_destroy((dom_node *) (n))
+#define dom_node_destroy(n) dom_node_destroy((dom_node_internal *) (n))
+/* Alloc the Node */
+static inline dom_exception dom_node_alloc(struct dom_document *doc,
+ struct dom_node_internal *n, struct dom_node_internal **ret)
+{
+ return ((dom_node_protect_vtable *) n->vtable)->alloc(doc, n, ret);
+}
+#define dom_node_alloc(d,n,r) dom_node_alloc((struct dom_document *) (d), \
+ (dom_node_internal *) (n), (dom_node_internal **) (r))
+
+
+/* Copy the Node old to new */
+static inline dom_exception dom_node_copy(struct dom_node_internal *new,
+ struct dom_node_internal *old)
+{
+ return ((dom_node_protect_vtable *) old->vtable)->copy(new, old);
+}
+#define dom_node_copy(n,o) dom_node_copy((dom_node_internal *) (n), \
+ (dom_node_internal *) (o))
+
+/* Following are some helper functions */
+#define dom_node_get_owner(n) ((dom_node_internal *) (n))->owner
+
+#define dom_node_set_owner(n, d) ((dom_node_internal *) (n))->owner = \
+ (struct dom_document *) (d)
+
+#define dom_node_get_parent(n) ((dom_node_internal *) (n))->parent
+
+#define dom_node_set_parent(n, p) ((dom_node_internal *) (n))->parent = \
+ (dom_node_internal *) (p)
+
+#define dom_node_get_refcount(n) ((dom_node_internal *) (n))->refcnt
+
+dom_exception _redocument_lwcstring(lwc_context *old, lwc_context *new,
+ lwc_string **string);
+dom_exception _redocument_domstring(struct dom_document *old,
+ struct dom_document* new, struct dom_string **string);
+dom_exception merge_adjacent_text(dom_node_internal *p, dom_node_internal *n);
+/* Used to extract the lwc_string from dom_string of function param.
+ * If there is no lwc_string inside the param, create one use the node->owner
+ * as document */
+dom_exception dom_node_get_intern_string(dom_node_internal *node,
+ struct dom_string *str, struct lwc_string_s **intern);
+void dom_node_unref_intern_string(dom_node_internal *node,
+ struct lwc_string_s *inter);
+
+/**
+ * Try to destroy the node, if its refcnt is not zeor, then append it to the
+ * owner document's pending list
+ */
+void _dom_node_try_destroy(dom_node_internal *node);
+#define dom_node_try_destroy(n) _dom_node_try_destroy((dom_node_internal *) (n))
+
+/* To add some node to the pending list */
+void _dom_node_mark_pending(dom_node_internal *node);
+#define dom_node_mark_pending(n) _dom_node_mark_pending((dom_node_internal *) (n))
+/* To remove the node from the pending list, this may happen when
+ * a node is removed and then appended to another parent */
+void _dom_node_remove_pending(dom_node_internal *node);
+#define dom_node_remove_pending(n) _dom_node_remove_pending((dom_node_internal *) (n))
+
#endif
Index: src/core/impllist.c
===================================================================
--- src/core/impllist.c (revision 8909)
+++ src/core/impllist.c (working copy)
@@ -9,6 +9,8 @@
#include <dom/core/implementation.h>
#include <dom/core/impllist.h>
+extern void dom_implementation_list_destroy(struct dom_implementation_list *list);
+
/**
* Claim a reference on a DOM implementation list
*
@@ -29,22 +31,8 @@
*/
void dom_implementation_list_unref(struct dom_implementation_list *list)
{
- struct dom_implementation_list_item *i, *j;
-
if (--list->refcnt == 0) {
- /* Destroy all list entries */
- for (i = list->head; i; i = j) {
- j = i->next;
-
- /* Unreference the implementation */
- dom_implementation_unref(i->impl);
-
- /* And free the entry */
- list->alloc(i, 0, list->pw);
- }
-
- /* Free the list object */
- list->alloc(list, 0, list->pw);
+ dom_implementation_list_destroy(list);
}
}
Index: src/core/namednodemap.h
===================================================================
--- src/core/namednodemap.h (revision 8909)
+++ src/core/namednodemap.h (working copy)
@@ -18,14 +18,54 @@
struct dom_namednodemap;
struct dom_string;
+struct nnm_operation {
+ dom_exception (*namednodemap_get_length)(void *priv,
+ unsigned long *length);
+
+ dom_exception (*namednodemap_get_named_item)(void *priv,
+ struct dom_string *name, struct dom_node **node);
+
+ dom_exception (*namednodemap_set_named_item)(void *priv,
+ struct dom_node *arg, struct dom_node **node);
+
+ dom_exception (*namednodemap_remove_named_item)(
+ void *priv, struct dom_string *name,
+ struct dom_node **node);
+
+ dom_exception (*namednodemap_item)(void *priv,
+ unsigned long index, struct dom_node **node);
+
+ dom_exception (*namednodemap_get_named_item_ns)(
+ void *priv, struct dom_string *namespace,
+ struct dom_string *localname, struct dom_node **node);
+
+ dom_exception (*namednodemap_set_named_item_ns)(
+ void *priv, struct dom_node *arg,
+ struct dom_node **node);
+
+ dom_exception (*namednodemap_remove_named_item_ns)(
+ void *priv, struct dom_string *namespace,
+ struct dom_string *localname, struct dom_node **node);
+
+ void (*namednodemap_destroy)(void *priv);
+
+ bool (*namednodemap_equal)(void *p1, void *p2);
+};
+
/* Create a namednodemap */
dom_exception dom_namednodemap_create(struct dom_document *doc,
- struct dom_node_internal *head, dom_node_type type,
+ void *priv, struct nnm_operation *opt,
struct dom_namednodemap **map);
+/* Update the private data */
+void dom_namednodemap_update(struct dom_namednodemap *map, void *priv);
-/* Match a namednodemap instance against a set of creation parameters */
-bool dom_namednodemap_match(struct dom_namednodemap *map,
- struct dom_node_internal *head, dom_node_type type);
+/* Test whether two maps are equal */
+bool _dom_namednodemap_equal(struct dom_namednodemap *m1,
+ struct dom_namednodemap *m2);
+#define dom_namednodemap_equal(m1, m2) _dom_namednodemap_equal( \
+ (struct dom_namednodemap *) (m1), \
+ (struct dom_namednodemap *) (m2))
+
#endif
Index: src/bootstrap/init_fini.c
===================================================================
--- src/bootstrap/init_fini.c (revision 8909)
+++ src/bootstrap/init_fini.c (working copy)
@@ -8,9 +8,11 @@
#include <stdbool.h>
#include <dom/bootstrap/init_fini.h>
+#include <dom/bootstrap/implregistry.h>
#include "core/document.h"
#include "utils/namespace.h"
+#include "bootstrap/implementation.h"
static bool __initialised;
@@ -32,7 +34,7 @@
return DOM_NO_ERR;
}
- err = _dom_document_initialise(alloc, pw);
+ err = dom_document_module_initialise(alloc, pw);
if (err != DOM_NO_ERR) {
return err;
}
@@ -42,6 +44,17 @@
return err;
}
+ err = dom_implregistry_dom_implementation_initialise(
+ alloc, pw);
+ if (err != DOM_NO_ERR) {
+ return err;
+ }
+
+ err = dom_implementation_initialise(alloc, pw);
+ if (err != DOM_NO_ERR) {
+ return err;
+ }
+
__initialised = true;
return DOM_NO_ERR;
@@ -63,12 +76,14 @@
return DOM_NO_ERR;
}
+ dom_implementation_finalise();
+
err = _dom_namespace_finalise();
if (err != DOM_NO_ERR) {
return err;
}
- err = _dom_document_finalise();
+ err = dom_document_module_finalise();
if (err != DOM_NO_ERR) {
return err;
}
Index: src/bootstrap/implregistry.c
===================================================================
--- src/bootstrap/implregistry.c (revision 8909)
+++ src/bootstrap/implregistry.c (working copy)
@@ -11,7 +11,10 @@
#include <dom/bootstrap/implregistry.h>
#include <dom/core/impllist.h>
+#include <dom/core/implementation.h>
+void dom_implementation_list_destroy(struct dom_implementation_list *list);
+
/**
* Item in list of registered DOM implementation sources
*/
@@ -23,14 +26,23 @@
};
static struct dom_impl_src_item *sources; /**< List of registered sources */
+static dom_alloc alloc;
+static void *pw;
+dom_exception dom_implregistry_dom_implementation_initialise(
+ dom_alloc allocator, void *ptr)
+{
+ alloc = allocator;
+ pw = ptr;
+
+ return DOM_NO_ERR;
+}
+
/**
* Retrieve a DOM implementation from the registry
*
* \param features String containing required features
* \param impl Pointer to location to receive implementation
- * \param alloc Function to (de)allocate memory
- * \param pw Pointer to client-specific private data
* \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion
*
* Any memory allocated by this call should be allocated using
@@ -43,16 +55,14 @@
*/
dom_exception dom_implregistry_get_dom_implementation(
struct dom_string *features,
- struct dom_implementation **impl,
- dom_alloc alloc, void *pw)
+ struct dom_implementation **impl)
{
struct dom_impl_src_item *item;
struct dom_implementation *found = NULL;
dom_exception err;
for (item = sources; item; item = item->next) {
- err = item->source->get_dom_implementation(features, &found,
- alloc, pw);
+ err = item->source->get_dom_implementation(features, &found);
if (err != DOM_NO_ERR)
return err;
@@ -71,8 +81,6 @@
*
* \param features String containing required features
* \param list Pointer to location to receive list
- * \param alloc Function to (de)allocate memory
- * \param pw Pointer to client-specific private data
* \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion
*
* Any memory allocated by this call should be allocated using
@@ -88,8 +96,7 @@
*/
dom_exception dom_implregistry_get_dom_implementation_list(
struct dom_string *features,
- struct dom_implementation_list **list,
- dom_alloc alloc, void *pw)
+ struct dom_implementation_list **list)
{
struct dom_implementation_list *l;
struct dom_impl_src_item *item;
@@ -100,16 +107,15 @@
return DOM_NO_MEM_ERR;
l->head = NULL;
- l->alloc = alloc;
- l->pw = pw;
l->refcnt = 1;
+ l->destroy = dom_implementation_list_destroy;
for (item = sources; item; item = item->next) {
struct dom_implementation_list *plist = NULL;
struct dom_implementation_list_item *plast = NULL;
err = item->source->get_dom_implementation_list(features,
- &plist, alloc, pw);
+ &plist);
if (err != DOM_NO_ERR) {
dom_implementation_list_unref(l);
return err;
@@ -156,12 +162,9 @@
* Register a DOM implementation source with the DOM library
*
* \param source The implementation source to register
- * \param alloc Memory (de)allocation function
- * \param pw Pointer to client-specific private data
* \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion.
*/
-dom_exception dom_register_source(struct dom_implementation_source *source,
- dom_alloc alloc, void *pw)
+dom_exception dom_register_source(struct dom_implementation_source *source)
{
struct dom_impl_src_item *item;
@@ -182,3 +185,26 @@
return DOM_NO_ERR;
}
+/**
+ * Destory a dom_implementation_list
+ *
+ * \param list The list to destory
+ */
+void dom_implementation_list_destroy(struct dom_implementation_list *list)
+{
+ struct dom_implementation_list_item *i, *j;
+
+ /* Destroy all list entries */
+ for (i = list->head; i; i = j) {
+ j = i->next;
+
+ /* Unreference the implementation */
+ dom_implementation_unref(i->impl);
+
+ /* And free the entry */
+ alloc(i, 0, pw);
+ }
+
+ /* Free the list object */
+ alloc(list, 0, pw);
+}
Index: src/bootstrap/Makefile
===================================================================
--- src/bootstrap/Makefile (revision 8909)
+++ src/bootstrap/Makefile (working copy)
@@ -1,3 +1,3 @@
-DIR_SOURCES := implregistry.c init_fini.c
+DIR_SOURCES := implregistry.c init_fini.c implementation.c
include build/makefiles/Makefile.subdir
Index: bindings/xml/xmlparser.h
===================================================================
--- bindings/xml/xmlparser.h (revision 8909)
+++ bindings/xml/xmlparser.h (working copy)
@@ -16,12 +16,14 @@
#include "xmlerror.h"
struct dom_document;
+struct lwc_context_s;
typedef struct dom_xml_parser dom_xml_parser;
/* Create an XML parser instance */
dom_xml_parser *dom_xml_parser_create(const char *enc, const char *int_enc,
- dom_alloc alloc, void *pw, dom_msg msg, void *mctx);
+ dom_alloc alloc, void *pw, dom_msg msg, void *mctx,
+ struct lwc_context_s *ctx);
/* Destroy an XML parser instance */
void dom_xml_parser_destroy(dom_xml_parser *parser);
Index: bindings/xml/Makefile
===================================================================
--- bindings/xml/Makefile (revision 8909)
+++ bindings/xml/Makefile (working copy)
@@ -1,5 +1,5 @@
ifeq ($(WITH_LIBXML_BINDING),yes)
- DIR_SOURCES := xmlbinding.c xmlparser.c
+ DIR_SOURCES := xmlparser.c
DIR_INSTALL_ITEMS := /include/dom/bindings/libxml:xmlbinding.h;xmlerror.h;xmlparser.h
Index: bindings/xml/xmlparser.c
===================================================================
--- bindings/xml/xmlparser.c (revision 8909)
+++ bindings/xml/xmlparser.c (working copy)
@@ -7,6 +7,7 @@
#include <stdbool.h>
#include <string.h>
+#include <assert.h>
#include <libxml/parser.h>
#include <libxml/SAX2.h>
@@ -15,10 +16,14 @@
#include <dom/bootstrap/implpriv.h>
#include <dom/dom.h>
+#include <libwapcaplet/libwapcaplet.h>
+
#include "xmlerror.h"
#include "xmlparser.h"
#include "utils.h"
+#include "core/document.h"
+
static void xml_parser_start_document(void *ctx);
static void xml_parser_end_document(void *ctx);
static void xml_parser_start_element_ns(void *ctx, const xmlChar *localname,
@@ -42,6 +47,8 @@
struct dom_node *parent, xmlNodePtr child);
static void xml_parser_add_entity_reference(dom_xml_parser *parser,
struct dom_node *parent, xmlNodePtr child);
+static void xml_parser_add_entity(dom_xml_parser *parser,
+ struct dom_node *parent, xmlNodePtr child);
static void xml_parser_add_comment(dom_xml_parser *parser,
struct dom_node *parent, xmlNodePtr child);
static void xml_parser_add_document_type(dom_xml_parser *parser,
@@ -97,6 +104,8 @@
dom_msg msg; /**< Informational message function */
void *mctx; /**< Pointer to client data */
+
+ struct lwc_context_s *ctx; /**< The lwc_context of the parser */
};
/**
@@ -153,11 +162,13 @@
* parser encoding is not yet implemented
*/
dom_xml_parser *dom_xml_parser_create(const char *enc, const char *int_enc,
- dom_alloc alloc, void *pw, dom_msg msg, void *mctx)
+ dom_alloc alloc, void *pw, dom_msg msg, void *mctx,
+ lwc_context *ctx)
{
dom_xml_parser *parser;
struct dom_string *features;
dom_exception err;
+ int ret;
UNUSED(enc);
UNUSED(int_enc);
@@ -176,6 +187,11 @@
return NULL;
}
+ /* Use options of parsing context, don't substiute entities */
+ ret = xmlCtxtUseOptions(parser->xml_ctx, XML_PARSE_DTDATTR |
+ XML_PARSE_DTDLOAD);
+ assert(ret == 0);
+
parser->doc = NULL;
parser->complete = false;
@@ -205,7 +221,7 @@
/* Now, try to get an appropriate implementation from the registry */
err = dom_implregistry_get_dom_implementation(features,
- &parser->impl, (dom_alloc) alloc, pw);
+ &parser->impl);
if (err != DOM_NO_ERR) {
dom_string_unref(features);
dom_string_unref(parser->udkey);
@@ -224,6 +240,8 @@
parser->msg = msg;
parser->mctx = mctx;
+ parser->ctx = ctx;
+
return parser;
}
@@ -240,9 +258,7 @@
xmlFreeParserCtxt(parser->xml_ctx);
- /** \todo Do we want to clean up the document here, too? */
- /* Obviously, document cleanup wouldn't happen if the client has
- * claimed the document from us via xml_parser_get_document() */
+ xmlFreeDoc(parser->xml_ctx->myDoc);
parser->alloc(parser, 0, parser->pw);
}
@@ -326,9 +342,8 @@
/* namespace */ NULL,
/* qname */ NULL,
/* doctype */ NULL,
- &doc,
- (dom_alloc) parser->alloc,
- parser->pw);
+ parser->alloc, parser->pw, parser->ctx,
+ &doc);
if (err != DOM_NO_ERR) {
parser->msg(DOM_MSG_CRITICAL, parser->mctx,
"Failed creating document");
@@ -620,6 +635,9 @@
case XML_DTD_NODE:
xml_parser_add_document_type(parser, parent, child);
break;
+ case XML_ENTITY_DECL:
+ xml_parser_add_entity(parser, parent, child);
+ break;
default:
parser->msg(DOM_MSG_NOTICE, parser->mctx,
"Unsupported node type: %s",
@@ -833,18 +851,29 @@
goto cleanup;
}
- /* And add attribute to the element */
- err = dom_element_set_attribute_node(el, attr, &prev_attr);
- if (err != DOM_NO_ERR) {
- dom_node_unref((struct dom_node *) attr);
- parser->msg(DOM_MSG_ERROR, parser->mctx,
- "Failed attaching attribute '%s'",
- a->name);
- goto cleanup;
+ if (a->ns == NULL) {
+ /* And add attribute to the element */
+ err = dom_element_set_attribute_node(el, attr, &prev_attr);
+ if (err != DOM_NO_ERR) {
+ dom_node_unref((struct dom_node *) attr);
+ parser->msg(DOM_MSG_ERROR, parser->mctx,
+ "Failed attaching attribute '%s'",
+ a->name);
+ goto cleanup;
+ }
+ } else {
+ err = dom_element_set_attribute_node_ns(el, attr, &prev_attr);
+ if (err != DOM_NO_ERR) {
+ dom_node_unref((struct dom_node *) attr);
+ parser->msg(DOM_MSG_ERROR, parser->mctx,
+ "Failed attaching attribute '%s'",
+ a->name);
+ goto cleanup;
+ }
}
/* We're not interested in the previous attribute (if any) */
- if (prev_attr != NULL)
+ if (prev_attr != NULL && prev_attr != attr)
dom_node_unref((struct dom_node *) prev_attr);
/* We're no longer interested in the attribute node */
@@ -1075,6 +1104,14 @@
dom_node_unref((struct dom_node *) entity);
}
+static void xml_parser_add_entity(dom_xml_parser *parser,
+ struct dom_node *parent, xmlNodePtr child)
+{
+ UNUSED(parser);
+ UNUSED(parent);
+ UNUSED(child);
+}
+
/**
* Add a comment to the DOM
*
@@ -1189,8 +1226,8 @@
/* Create doctype */
err = dom_implementation_create_document_type(parser->impl,
- qname, public_id, system_id, &doctype,
- (dom_alloc) parser->alloc, parser->pw);
+ qname, public_id, system_id,
+ parser->alloc, parser->pw, parser->ctx, &doctype);
if (err != DOM_NO_ERR) {
dom_string_unref(system_id);
dom_string_unref(public_id);
Index: bindings/hubbub/parser.h
===================================================================
--- bindings/hubbub/parser.h (revision 8909)
+++ bindings/hubbub/parser.h (working copy)
@@ -16,6 +16,7 @@
#include "errors.h"
struct dom_document;
+struct lwc_context_s;
typedef struct dom_hubbub_parser dom_hubbub_parser;
@@ -50,7 +51,8 @@
/* Create a Hubbub parser instance */
dom_hubbub_parser *dom_hubbub_parser_create(const char *aliases,
const char *enc, bool fix_enc,
- dom_alloc alloc, void *pw, dom_msg msg, void *mctx);
+ dom_alloc alloc, void *pw, dom_msg msg, void *mctx,
+ struct lwc_context_s *ctx);
/* Destroy a Hubbub parser instance */
void dom_hubbub_parser_destroy(dom_hubbub_parser *parser);
Index: bindings/hubbub/parser.c
===================================================================
--- bindings/hubbub/parser.c (revision 8909)
+++ bindings/hubbub/parser.c (working copy)
@@ -8,7 +8,6 @@
#include <stdio.h>
-#include <hubbub/errors.h>
#include <hubbub/hubbub.h>
#include <hubbub/parser.h>
@@ -17,6 +16,8 @@
#include "parser.h"
#include "utils.h"
+#include <libwapcaplet/libwapcaplet.h>
+
/**
* libdom Hubbub parser context
*/
@@ -40,15 +41,17 @@
dom_msg msg; /**< Informational messaging function */
void *mctx; /**< Pointer to client data */
+
+ struct lwc_context_s *ctx; /**< The string intern context */
};
-static hubbub_error create_comment(void *parser, const hubbub_string *data,
+/* The callbacks declarations */
+static hubbub_error create_comment(void *parser, const hubbub_string *data,
void **result);
static hubbub_error create_doctype(void *parser, const hubbub_doctype *doctype,
void **result);
-static hubbub_error create_element(void *parser, const hubbub_tag *tag,
- void **result);
+static hubbub_error create_element(void *parser, const hubbub_tag *tag, void **result);
static hubbub_error create_text(void *parser, const hubbub_string *data,
void **result);
static hubbub_error ref_node(void *parser, void *node);
@@ -59,10 +62,8 @@
void *ref_child, void **result);
static hubbub_error remove_child(void *parser, void *parent, void *child,
void **result);
-static hubbub_error clone_node(void *parser, void *node, bool deep,
- void **result);
-static hubbub_error reparent_children(void *parser, void *node,
- void *new_parent);
+static hubbub_error clone_node(void *parser, void *node, bool deep, void **result);
+static hubbub_error reparent_children(void *parser, void *node, void *new_parent);
static hubbub_error get_parent(void *parser, void *node, bool element_only,
void **result);
static hubbub_error has_children(void *parser, void *node, bool *result);
@@ -109,7 +110,7 @@
*/
dom_hubbub_parser *dom_hubbub_parser_create(const char *aliases,
const char *enc, bool fix_enc,
- dom_alloc alloc, void *pw, dom_msg msg, void *mctx)
+ dom_alloc alloc, void *pw, dom_msg msg, void *mctx, lwc_context *ctx)
{
dom_hubbub_parser *parser;
hubbub_parser_optparams params;
@@ -118,7 +119,7 @@
struct dom_string *features;
if (__initialised == false) {
- error = hubbub_initialise(aliases, alloc, pw);
+ error = hubbub_initialise(aliases, (hubbub_allocator_fn) alloc, pw);
if (error != HUBBUB_OK) {
msg(DOM_MSG_ERROR, mctx,
"Failed initialising hubbub");
@@ -147,6 +148,7 @@
parser->pw = pw;
parser->msg = msg;
parser->mctx = mctx;
+ parser->ctx = ctx;
error = hubbub_parser_create(enc, fix_enc, alloc, pw, &parser->parser);
if (error != HUBBUB_OK) {
@@ -168,7 +170,7 @@
/* Now, try to get an appropriate implementation from the registry */
err = dom_implregistry_get_dom_implementation(features,
- &parser->impl, alloc, pw);
+ &parser->impl);
if (err != DOM_NO_ERR) {
dom_string_unref(features);
hubbub_parser_destroy(parser->parser);
@@ -181,7 +183,7 @@
dom_string_unref(features);
err = dom_implementation_create_document(parser->impl, NULL, NULL, NULL,
- &parser->doc, alloc, pw);
+ alloc, pw, ctx, &parser->doc);
if (err != DOM_NO_ERR) {
hubbub_parser_destroy(parser->parser);
alloc(parser, 0, pw);
@@ -269,7 +271,7 @@
}
/* The callbacks definitions */
-hubbub_error create_comment(void *parser, const hubbub_string *data,
+static hubbub_error create_comment(void *parser, const hubbub_string *data,
void **result)
{
dom_hubbub_parser *dom_parser = (dom_hubbub_parser *) parser;
@@ -284,7 +286,7 @@
if (err != DOM_NO_ERR) {
dom_parser->msg(DOM_MSG_CRITICAL, dom_parser->mctx,
"Can't create comment node text");
- return HUBBUB_NOMEM;
+ return HUBBUB_UNKNOWN;
}
err = dom_document_create_comment(dom_parser->doc, str, &comment);
@@ -293,7 +295,7 @@
dom_parser->msg(DOM_MSG_CRITICAL, dom_parser->mctx,
"Can't create comment node with text '%.*s'",
data->len, data->ptr);
- return HUBBUB_NOMEM;
+ return HUBBUB_UNKNOWN;
}
*result = comment;
@@ -303,7 +305,7 @@
return HUBBUB_OK;
}
-hubbub_error create_doctype(void *parser, const hubbub_doctype *doctype,
+static hubbub_error create_doctype(void *parser, const hubbub_doctype *doctype,
void **result)
{
dom_hubbub_parser *dom_parser = (dom_hubbub_parser *) parser;
@@ -350,8 +352,8 @@
}
err = dom_implementation_create_document_type(dom_parser->impl, qname,
- public_id, system_id, &dtype, dom_parser->alloc,
- dom_parser->pw);
+ public_id, system_id, dom_parser->alloc,
+ dom_parser->pw, dom_parser->ctx, &dtype);
if (err != DOM_NO_ERR) {
dom_parser->msg(DOM_MSG_CRITICAL, dom_parser->mctx,
"Can't create the document type");
@@ -371,17 +373,18 @@
fail:
if (*result == NULL)
- return HUBBUB_NOMEM;
+ return HUBBUB_UNKNOWN;
else
return HUBBUB_OK;
}
-hubbub_error create_element(void *parser, const hubbub_tag *tag, void **result)
+static hubbub_error create_element(void *parser, const hubbub_tag *tag, void **result)
{
dom_hubbub_parser *dom_parser = (dom_hubbub_parser *) parser;
dom_exception err;
struct dom_string *name;
struct dom_element *element = NULL;
+ hubbub_error herr;
*result = NULL;
@@ -412,17 +415,26 @@
}
*result = element;
+ if (element != NULL) {
+ if (tag->n_attributes != 0) {
+ herr = add_attributes(parser, element, tag->attributes,
+ tag->n_attributes);
+ if (herr != HUBBUB_OK)
+ return herr;
+ }
+ }
+
clean1:
dom_string_unref(name);
fail:
if (*result == NULL)
- return HUBBUB_NOMEM;
+ return HUBBUB_UNKNOWN;
else
return HUBBUB_OK;
}
-hubbub_error create_text(void *parser, const hubbub_string *data, void **result)
+static hubbub_error create_text(void *parser, const hubbub_string *data, void **result)
{
dom_hubbub_parser *dom_parser = (dom_hubbub_parser *) parser;
dom_exception err;
@@ -453,13 +465,13 @@
fail:
if (*result == NULL)
- return HUBBUB_NOMEM;
+ return HUBBUB_UNKNOWN;
else
return HUBBUB_OK;
}
-hubbub_error ref_node(void *parser, void *node)
+static hubbub_error ref_node(void *parser, void *node)
{
struct dom_node *dnode = (struct dom_node *) node;
@@ -470,7 +482,7 @@
return HUBBUB_OK;
}
-hubbub_error unref_node(void *parser, void *node)
+static hubbub_error unref_node(void *parser, void *node)
{
struct dom_node *dnode = (struct dom_node *) node;
@@ -481,7 +493,7 @@
return HUBBUB_OK;
}
-hubbub_error append_child(void *parser, void *parent, void *child, void **result)
+static hubbub_error append_child(void *parser, void *parent, void *child, void **result)
{
dom_hubbub_parser *dom_parser = (dom_hubbub_parser *) parser;
dom_exception err;
@@ -493,13 +505,13 @@
dom_parser->msg(DOM_MSG_CRITICAL, dom_parser->mctx,
"Can't append child '%p' for parent '%p'",
child, parent);
- return HUBBUB_NOMEM;
+ return HUBBUB_UNKNOWN;
}
return HUBBUB_OK;
}
-hubbub_error insert_before(void *parser, void *parent, void *child,
+static hubbub_error insert_before(void *parser, void *parent, void *child,
void *ref_child, void **result)
{
dom_hubbub_parser *dom_parser = (dom_hubbub_parser *) parser;
@@ -513,13 +525,13 @@
dom_parser->msg(DOM_MSG_CRITICAL, dom_parser->mctx,
"Can't insert node '%p' before node '%p'",
child, ref_child);
- return HUBBUB_NOMEM;
+ return HUBBUB_UNKNOWN;
}
return HUBBUB_OK;
}
-hubbub_error remove_child(void *parser, void *parent, void *child, void **result)
+static hubbub_error remove_child(void *parser, void *parent, void *child, void **result)
{
dom_hubbub_parser *dom_parser = (dom_hubbub_parser *) parser;
dom_exception err;
@@ -530,13 +542,13 @@
if (err != DOM_NO_ERR) {
dom_parser->msg(DOM_MSG_CRITICAL, dom_parser->mctx,
"Can't remove child '%p'", child);
- return HUBBUB_NOMEM;
+ return HUBBUB_UNKNOWN;
}
return HUBBUB_OK;
}
-hubbub_error clone_node(void *parser, void *node, bool deep, void **result)
+static hubbub_error clone_node(void *parser, void *node, bool deep, void **result)
{
dom_hubbub_parser *dom_parser = (dom_hubbub_parser *) parser;
dom_exception err;
@@ -546,13 +558,13 @@
if (err != DOM_NO_ERR) {
dom_parser->msg(DOM_MSG_CRITICAL, dom_parser->mctx,
"Can't clone node '%p'", node);
- return HUBBUB_NOMEM;
+ return HUBBUB_UNKNOWN;
}
return HUBBUB_OK;
}
-hubbub_error reparent_children(void *parser, void *node, void *new_parent)
+static hubbub_error reparent_children(void *parser, void *node, void *new_parent)
{
dom_hubbub_parser *dom_parser = (dom_hubbub_parser *) parser;
dom_exception err;
@@ -564,7 +576,7 @@
if (err != DOM_NO_ERR) {
dom_parser->msg(DOM_MSG_CRITICAL, dom_parser->mctx,
"Error in dom_note_get_first_child");
- return HUBBUB_NOMEM;
+ return HUBBUB_UNKNOWN;
}
if (child == NULL)
break;
@@ -592,10 +604,10 @@
fail:
dom_node_unref(child);
- return HUBBUB_NOMEM;
+ return HUBBUB_UNKNOWN;
}
-hubbub_error get_parent(void *parser, void *node, bool element_only,
+static hubbub_error get_parent(void *parser, void *node, bool element_only,
void **result)
{
dom_hubbub_parser *dom_parser = (dom_hubbub_parser *) parser;
@@ -608,7 +620,7 @@
if (err != DOM_NO_ERR) {
dom_parser->msg(DOM_MSG_CRITICAL, dom_parser->mctx,
"Error in dom_node_get_parent");
- return HUBBUB_NOMEM;
+ return HUBBUB_UNKNOWN;
}
if (element_only == false) {
*result = parent;
@@ -623,18 +635,19 @@
}
if (type == DOM_ELEMENT_NODE) {
*result = parent;
+ return HUBBUB_OK;
} else {
*result = NULL;
dom_node_unref(parent);
+ return HUBBUB_OK;
}
- return HUBBUB_OK;
fail:
dom_node_unref(parent);
- return HUBBUB_NOMEM;
+ return HUBBUB_UNKNOWN;
}
-hubbub_error has_children(void *parser, void *node, bool *result)
+static hubbub_error has_children(void *parser, void *node, bool *result)
{
dom_hubbub_parser *dom_parser = (dom_hubbub_parser *) parser;
dom_exception err;
@@ -645,13 +658,12 @@
if (err != DOM_NO_ERR) {
dom_parser->msg(DOM_MSG_CRITICAL, dom_parser->mctx,
"Error in dom_node_has_child_nodes");
- return HUBBUB_NOMEM;
+ return HUBBUB_UNKNOWN;
}
-
return HUBBUB_OK;
}
-hubbub_error form_associate(void *parser, void *form, void *node)
+static hubbub_error form_associate(void *parser, void *form, void *node)
{
UNUSED(parser);
UNUSED(form);
@@ -660,7 +672,7 @@
return HUBBUB_OK;
}
-hubbub_error add_attributes(void *parser, void *node,
+static hubbub_error add_attributes(void *parser, void *node,
const hubbub_attribute *attributes, uint32_t n_attributes)
{
dom_hubbub_parser *dom_parser = (dom_hubbub_parser *) parser;
@@ -719,10 +731,10 @@
return HUBBUB_OK;
fail:
- return HUBBUB_NOMEM;
+ return HUBBUB_UNKNOWN;
}
-hubbub_error set_quirks_mode(void *parser, hubbub_quirks_mode mode)
+static hubbub_error set_quirks_mode(void *parser, hubbub_quirks_mode mode)
{
UNUSED(parser);
UNUSED(mode);
@@ -730,7 +742,7 @@
return HUBBUB_OK;
}
-hubbub_error change_encoding(void *parser, const char *charset)
+static hubbub_error change_encoding(void *parser, const char *charset)
{
dom_hubbub_parser *dom_parser = (dom_hubbub_parser *) parser;
uint32_t source;
Index: Makefile
===================================================================
--- Makefile (revision 8909)
+++ Makefile (working copy)
@@ -47,7 +47,8 @@
INSTALL_ITEMS := $(INSTALL_ITEMS) $(I):$(Is)/implementation.h;$(Is)/impllist.h
INSTALL_ITEMS := $(INSTALL_ITEMS) $(I):$(Is)/namednodemap.h;$(Is)/node.h
INSTALL_ITEMS := $(INSTALL_ITEMS) $(I):$(Is)/nodelist.h;$(Is)/string.h
-INSTALL_ITEMS := $(INSTALL_ITEMS) $(I):$(Is)/text.h
+INSTALL_ITEMS := $(INSTALL_ITEMS) $(I):$(Is)/text.h;$(Is)/typeinfo.h
+INSTALL_ITEMS := $(INSTALL_ITEMS) $(I):$(Is)/comment.h
INSTALL_ITEMS := $(INSTALL_ITEMS) /lib/pkgconfig:lib$(COMPONENT).pc.in
INSTALL_ITEMS := $(INSTALL_ITEMS) /lib:$(OUTPUT)
Conflicted files
Removed files
test/testutils.h
test/binding.c
test/xml
test/xml/tests
test/xml/tests/dom1.dtd
test/lib
test/lib/comparators.h
test/lib/list.h
test/lib/utils.c
test/lib/testassert.c
test/lib/utils.h
test/lib/exceptions.h
test/lib/testassert.h
test/lib/testobject.c
test/lib/comparators.c
test/lib/list.c
test/lib/testobject.h
test/INDEX
test/test-list.c
test/data/binding
test/data/binding/Aliases
test/data/binding/staff.xml
test/data/binding/sample.html
test/transform
test/transform/dom1-interfaces.xml
test/transform/test-to-c.xsl
test/transform/ctypes.xml
test/transform/string.xsl
bindings/xml/xmlbinding.h
bindings/xml/xmlbinding.c
14 years, 1 month
Review: Mark Benjamin -- gtkmain branch
by John-Mark Bell
Precis:
This is Mark Benjamin's GTK work. It builds for GTK, with warnings. It does not build for RISC OS. The Amiga frontend conflicts loads when merged to trunk, so all bets are off there.
GTK build warnings:
COMPILE: desktop/save_complete.c
desktop/save_complete.c: In function âsave_complete_htmlâ:
desktop/save_complete.c:150: warning: cast from pointer to integer of different size
desktop/save_complete.c:185: warning: cast from pointer to integer of different size
desktop/save_complete.c:212: warning: cast from pointer to integer of different size
desktop/save_complete.c: In function âsave_imported_sheetsâ:
desktop/save_complete.c:263: warning: cast from pointer to integer of different size
desktop/save_complete.c: In function ârewrite_stylesheet_urlsâ:
desktop/save_complete.c:417: warning: cast from pointer to integer of different size
desktop/save_complete.c: In function ârewrite_urlâ:
desktop/save_complete.c:619: warning: cast from pointer to integer of different size
desktop/save_complete.c: In function âsave_complete_inventoryâ:
desktop/save_complete.c:735: warning: cast from pointer to integer of different size
COMPILE: gtk/gtk_toolbar.c
gtk/gtk_toolbar.c: In function ânsgtk_toolbar_customization_loadâ:
gtk/gtk_toolbar.c:896: warning: âpterâ may be used uninitialised in this function
COMPILE: render/favicon.c
render/favicon.c: In function âfavicon_get_iconâ:
render/favicon.c:284: warning: passing argument 11 of âfetchcache_goâ from incompatible pointer type
RISC OS build warnings + failure:
COMPILE: content/fetchcache.c
content/fetchcache.c: In function 'fetchcache_search_redirect':
content/fetchcache.c:833: warning: passing argument 11 of 'fetchcache_go' from incompatible pointer type
COMPILE: render/favicon.c
render/favicon.c: In function 'favicon_get_icon':
render/favicon.c:284: warning: passing argument 11 of 'fetchcache_go' from incompatible pointer type
COMPILE: riscos/search.c
riscos/search.c:188: error: conflicting types for 'ro_gui_search_prepare'
./riscos/gui.h:202: error: previous declaration of 'ro_gui_search_prepare' was here
Added files
Index: render/favicon.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ render/favicon.c 2009-07-10 12:49:13.000000000 +0100
@@ -0,0 +1,375 @@
+/*
+ * Copyright 2007 James Bursa <bursa(a)users.sourceforge.net>
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin(a)dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/ >.
+ */
+
+#include <string.h>
+#include "content/fetch.h"
+#include "content/fetchcache.h"
+#include "render/favicon.h"
+#include "render/html.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/talloc.h"
+#include "utils/url.h"
+#include "utils/utils.h"
+
+static char *favicon_get_icon_ref(struct content *c, xmlNode *html);
+static void favicon_callback(content_msg msg, struct content *icon,
+ intptr_t p1, intptr_t p2, union content_msg_data data);
+static unsigned long favicon_hash(char *str);
+
+unsigned long favicon_hash(char *str)
+{
+ if (str == NULL)
+ return 0;
+ unsigned long hash = 5381;
+ int c;
+ while ((c = (unsigned char) *str++))
+ hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
+ return hash;
+}
+
+/**
+ * retrieve 1 url reference to 1 favicon
+ * \param html xml node of html element
+ * \return pointer to url; NULL for no icon
+ */
+char *favicon_get_icon_ref(struct content *c, xmlNode *html)
+{
+ xmlNode *node;
+ char *rel, *type, *href, *url, *suf, *url2;
+ url2 = NULL;
+ url_func_result res;
+ int score, hiscore;
+ hiscore = 0;
+ /* hashed values - saves calculating them afresh every time */
+ #define HHICON 0x7c98572e
+ /* icon */
+ #define HHSHORTCUTICON 0xfcbccdca
+ /* shortcut icon */
+ #define HHAPPLETOUCHICON 0x024c6ddd
+ /* apple-touch-icon */
+ #define HHIMAGEPNG 0x7382417c
+ /* image/png */
+ #define HHIMAGEGIF 0x73821a8d
+ /* image/gif */
+ #define HHIMAGEVNDMICROSOFTICON 0xdae02bba
+ /* image.vnd.microsoft.icon */
+ #define HHIMAGEJPEG 0xe3c72f5d
+ /* image/jpeg */
+ #define HHIMAGEJPG 0x73822838
+ /* image/jpg */
+ #define HHIMAGEICO 0x73822252
+ /* image/ico */
+ #define HHIMAGEICON 0xe3c66d00
+ /* image/icon */
+ #define HHIMAGEXICON 0x0e3e78e5
+ /* image/x-icon */
+ #define HHTEXTICO 0x17e966a2
+ /* text/icon */
+ #define HHAPPLICATIONICO 0x087b6fb4
+ /*application/icon*/
+ #define HHSUFICO 0x0b887ec0
+ /* ico */
+ #define HHSUFPNG 0x0b889dea
+ /* png */
+ #define HHSUFGIF 0x0b8876fb
+ /* gif */
+ #define HHSUFJPG 0x0b888486
+ /* jpg */
+ #define HHSUFJPEG 0x7c99198b
+ /* jpeg */
+
+ union content_msg_data msg_data;
+
+ node = html;
+ while (node) {
+ score = 0;
+ suf = NULL;
+ if (node->children) { /* children */
+ node = node->children;
+ } else if (node->next) { /* siblings */
+ node = node->next;
+ } else { /* ancestor siblings */
+ while (node && !node->next)
+ node = node->parent;
+ if (!node)
+ break;
+ node = node->next;
+ }
+ assert(node);
+
+ if (node->type != XML_ELEMENT_NODE)
+ continue;
+
+ if (strcmp((const char *) node->name, "link") == 0) {
+ /* rel=<space separated list, including 'icon'> */
+ if ((rel = (char *) xmlGetProp(node,
+ (const xmlChar *) "rel")) == NULL)
+ continue;
+ if (strcasestr(rel, "icon") == 0) {
+ xmlFree(rel);
+ continue;
+ }
+ LOG(("icon node found"));
+ switch(favicon_hash(rel)) {
+ /* give points for rel attributes, kind of arbitrary
+ * in an attempt to test how closely standards are
+ * being respected; the reason apple-touch-icon scores
+ * less is that the appearance of such is really
+ * specific to the iphone style */
+ case HHICON:
+ LOG(("icon"));
+ score = 3;
+ break;
+ case HHSHORTCUTICON:
+ LOG(("shortcut icon"));
+ score = 5;
+ break;
+ case HHAPPLETOUCHICON:
+ LOG(("apple-touch-icon"));
+ score = 1;
+ break;
+ }
+ xmlFree(rel);
+ /* current implementation scores candidates according
+ * to how closely they seem to adhere to standards,
+ * scoring system may be modified in the future */
+ if ((type = (char *) xmlGetProp(node,
+ (const xmlChar *) "type")) != NULL) {
+ switch(favicon_hash(type)) {
+ /* here we score highest for "image/type"
+ * mime types, lower scores for "type/ico"
+ * mime types, no score for no type */
+ case HHIMAGEPNG:
+ score += 5;
+ break;
+ case HHIMAGEGIF:
+ score += 5;
+ break;
+ case HHIMAGEVNDMICROSOFTICON:
+ score += 5;
+ break;
+ case HHIMAGEJPEG:
+ score += 5;
+ break;
+ case HHIMAGEJPG:
+ score += 5;
+ break;
+ case HHIMAGEICO:
+ score += 5;
+ break;
+ case HHIMAGEICON:
+ score += 5;
+ break;
+ case HHIMAGEXICON:
+ score += 5;
+ break;
+ case HHTEXTICO:
+ score += 2;
+ break;
+ case HHAPPLICATIONICO:
+ score += 1;
+ break;
+ }
+ xmlFree(type);
+ }
+ if ((href = (char *) xmlGetProp(node,
+ (const xmlChar *) "href")) == NULL)
+ continue;
+ suf = strrchr(href, '.');
+ if (suf != NULL) {
+ suf ++;
+ switch(favicon_hash(suf)) {
+ /* here the largest bonus points of all
+ * attributes, notably for .ico, .png, .gif
+ * as the standards support; less for .jpg */
+ case HHSUFICO:
+ score += 10;
+ break;
+ case HHSUFPNG:
+ score += 10;
+ break;
+ case HHSUFGIF:
+ score += 10;
+ break;
+ case HHSUFJPG:
+ score += 7;
+ break;
+ case HHSUFJPEG:
+ score += 7;
+ break;
+ }
+ }
+ if (score > hiscore) {
+ res = url_join(href, c->data.html.base_url,
+ &url);
+ xmlFree(href);
+ if (res != URL_FUNC_OK)
+ continue;
+
+ LOG(("best favicon so far '%s'", url));
+ res = url_normalize(url, &url2);
+ if (res != URL_FUNC_OK) {
+ url2 = NULL;
+ if (res == URL_FUNC_NOMEM)
+ goto no_memory;
+ continue;
+ }
+ hiscore = score;
+ free(url);
+ }
+ }
+ }
+ if (url2 == NULL) {
+ struct url_components comp;
+ if (url_get_components(c->data.html.base_url, &comp) !=
+ URL_FUNC_OK)
+ return NULL;
+ if (url_normalize(comp.authority,
+ &url) != URL_FUNC_OK)
+ return NULL;
+ url_destroy_components(&comp);
+ if (url_join("/favicon.ico", url, &url2) != URL_FUNC_OK)
+ return NULL;
+ free(url);
+ }
+ LOG(("Best favicon %s", url2));
+ return url2;
+no_memory:
+ msg_data.error = messages_get("NoMemory");
+ /* content_broadcast(c, CONTENT_MSG_ERROR, msg_data); */
+ return false;
+}
+
+/**
+ * retrieve 1 favicon
+ * \param c content structure
+ * \param html xml node of html element
+ * \return true for success, false for error
+ */
+
+bool favicon_get_icon(struct content *c, xmlNode *html)
+{
+ union content_msg_data msg_data;
+ char *url = favicon_get_icon_ref(c, html);
+ struct content *favcontent = NULL;
+ if (url != NULL)
+ favcontent = fetchcache(url, favicon_callback,
+ (intptr_t) c, 0, c->width, c->height, true, 0,
+ 0, false, false);
+ free(url);
+ if (favcontent == NULL)
+ return false;
+
+ c->data.html.favicon = favcontent;
+
+ fetchcache_go(favcontent, c->url, favicon_callback,
+ (intptr_t) c, 0, c->width, c->height,
+ 0, 0, false, c->url);
+
+ if (!c->data.html.favicon) {
+ msg_data.error = "Favicon failed to load";
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Callback for fetchcache() for linked favicon
+ */
+
+void favicon_callback(content_msg msg, struct content *icon,
+ intptr_t p1, intptr_t p2, union content_msg_data data)
+{
+ struct content *c = (struct content *) p1;
+ unsigned int i = p2;
+
+ switch (msg) {
+ case CONTENT_MSG_LOADING:
+ /* check that the favicon is really a correct image type */
+
+ if (!((icon->type == CONTENT_ICO) ||
+ (icon->type == CONTENT_PNG) ||
+ (icon->type == CONTENT_GIF))) {
+ c->data.html.favicon = 0;
+ LOG(("%s is not a favicon", icon->url));
+ content_add_error(c, "NotFavIco", 0);
+ html_set_status(c, messages_get("NotFavIco"));
+ content_broadcast(c, CONTENT_MSG_STATUS, data);
+ content_remove_user(icon,
+ favicon_callback,
+ (intptr_t) c, i);
+ if (!icon->user_list->next) {
+ /* we were the only user and we don't want this
+ * content, so stop it fetching and mark it as
+ * having an error so it gets removed from the
+ * cache next time content_clean() gets called
+ */
+ fetch_abort(icon->fetch);
+ icon->fetch = 0;
+ icon->status = CONTENT_STATUS_ERROR;
+ }
+ }
+ break;
+
+ case CONTENT_MSG_READY:
+ break;
+
+ case CONTENT_MSG_DONE:
+ LOG(("got favicon '%s'", icon->url));
+ break;
+
+ case CONTENT_MSG_LAUNCH:
+ /* Fall through */
+ case CONTENT_MSG_ERROR:
+ LOG(("favicon %s failed: %s", icon->url, data.error));
+ /* The favicon we were fetching may have been
+ * redirected, in that case, the object pointers
+ * will differ, so ensure that the object that's
+ * in error is still in use by us before invalidating
+ * the pointer */
+ if (c->data.html.favicon == icon) {
+ c->data.html.favicon = 0;
+ content_add_error(c, "?", 0);
+ }
+ break;
+
+ case CONTENT_MSG_STATUS:
+ html_set_status(c, icon->status_message);
+ content_broadcast(c, CONTENT_MSG_STATUS, data);
+ break;
+
+ case CONTENT_MSG_NEWPTR:
+ c->data.html.favicon = icon;
+ break;
+
+ case CONTENT_MSG_AUTH:
+ c->data.html.favicon = 0;
+ content_add_error(c, "?", 0);
+ break;
+
+ case CONTENT_MSG_SSL:
+ c->data.html.favicon = 0;
+ content_add_error(c, "?", 0);
+ break;
+
+ default:
+ assert(0);
+ }
+}
Index: render/favicon.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ render/favicon.h 2009-07-10 12:49:13.000000000 +0100
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin(a)dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/ >.
+ */
+
+#ifndef _NETSURF_RENDER_FAVICON_H_
+#define _NETSURF_RENDER_FAVICON_H_
+
+typedef enum {
+ HHICON = 0x7c98572e,
+ /* icon */
+ HHSHORTCUTICON = 0xfcbccdca,
+ /* shortcut icon */
+ HHAPPLETOUCHICON = 0x024c6ddd,
+ /* apple-touch-icon */
+ HHIMAGEPNG = 0x7382417c,
+ /* image/png */
+ HHIMAGEGIF = 0x73821a8d,
+ /* image/gif */
+ HHIMAGEVNDMICROSOFTICON = 0xdae02bba,
+ /* image.vnd.microsoft.icon */
+ HHIMAGEJPEG = 0xe3c72f5d,
+ /* image/jpeg */
+ HHIMAGEJPG = 0x73822838,
+ /* image/jpg */
+ HHIMAGEICO = 0x73822252,
+ /* image/ico */
+ HHIMAGEICON = 0xe3c66d00,
+ /* image/icon */
+ HHIMAGEXICON = 0x0e3e78e5,
+ /* image/x-icon */
+ HHTEXTICO = 0x17e966a2,
+ /* text/icon */
+ HHAPPLICATIONICO = 0x087b6fb4,
+ /* application/icon */
+ HHSUFICO = 0x0b887ec0,
+ /* ico */
+ HHSUFPNG = 0x0b889dea,
+ /* png */
+ HHSUFGIF = 0x0b8876fb,
+ /* gif */
+ HHSUFJPG = 0x0b888486,
+ /* jpg */
+ HHSUFJPEG = 0x7c99198b
+ /* jpeg */
+} favicon_string_hash;
+
+bool favicon_get_icon(struct content *c, xmlNode *html);
+
+#endif
Index: framebuffer/fb_search.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ framebuffer/fb_search.c 2009-07-10 12:49:34.000000000 +0100
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin(a)dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/ >.
+ */
+
+#include <stdbool.h>
+#include <string.h>
+
+#include "utils/log.h"
+
+/* put new search_web globals here for now */
+char *search_engines_file_location;
+char *search_default_ico_location;
+
+/**
+ * Change the displayed search status.
+ * \param found search pattern matched in text
+ */
+void gui_search_set_status(bool found)
+{
+}
+
+/**
+ * display hourglass while searching
+ * \param active start/stop indicator
+ */
+void gui_search_set_hourglass(bool active)
+{
+}
+
+/**
+ * retrieve string being searched for from gui
+ */
+char *gui_search_get_string(void)
+{
+}
+
+/**
+ * add search string to recent searches list
+ * \param string search pattern
+ */
+void gui_search_add_recent(const char *string)
+{
+}
+
+/**
+ * activate search forwards button in gui
+ * \param active activate/inactivate
+ */
+void gui_search_set_forward_state(bool inactive)
+{
+}
+
+/**
+ * activate search forwards button in gui
+ * \param active activate/inactivate
+ */
+void gui_search_set_back_state(bool inactive)
+{
+}
+
+/**
+ * retrieve state of 'case sensitive' check in gui
+ */
+bool gui_search_get_case_sens(void)
+{
+}
+
+/**
+ * retrieve state of 'show all' check in gui
+ */
+bool gui_search_get_show_all(void)
+{
+}
Index: gtk/gtk_save.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ gtk/gtk_save.c 2009-07-10 12:49:36.000000000 +0100
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin(a)dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/ >.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <libxml/HTMLtree.h>
+#include "desktop/save_complete.h"
+#include "utils/utils.h"
+
+bool save_complete_gui_save(const char *path, const char *filename, struct content *c, int len, char *sourcedata, int type)
+{
+ int res;
+ int namelen;
+ namelen = strlen(path) + strlen(filename) + 2;
+ char *fullpath = malloc(namelen);
+ if (!fullpath) {
+ warn_user("NoMemory", 0);
+ return false;
+ }
+ snprintf(fullpath, namelen, "%s/%s", path, filename);
+ FILE *f = fopen(fullpath, "w"); /* may need mode 'b' when c != NULL */
+ free(fullpath);
+ if (f == NULL)
+ return false;
+ res = fwrite(sourcedata, len, 1, f);
+ fclose(f);
+ if (res != 1)
+ return false;
+ return true;
+}
+
+int save_complete_htmlSaveFileFormat(const char *path, const char *filename,
+ xmlDocPtr cur, const char *encoding, int format)
+{
+ int ret;
+ int len = strlen(path) + strlen(filename) + 2;
+ char *finame = malloc(len);
+ if (!finame){
+ warn_user("NoMemory", 0);
+ return -1;
+ }
+ snprintf(finame, len, "%s/%s", path, filename);
+ ret = htmlSaveFileFormat(finame, cur, encoding, format);
+ free(finame);
+ return ret;
+}
+
+bool save_complete_gui_filetype(const char *path, const char *filename,
+ int type)
+{
+ return true;
+}
Index: gtk/gtk_theme.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ gtk/gtk_theme.c 2009-07-10 12:49:36.000000000 +0100
@@ -0,0 +1,591 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin(a)dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/ >.
+ */
+
+#include <gtk/gtk.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include "content/content.h"
+#include "content/content_type.h"
+#include "gtk/gtk_gui.h"
+#include "gtk/gtk_scaffolding.h"
+#include "gtk/gtk_menu.h"
+#include "gtk/gtk_theme.h"
+#include "gtk/gtk_window.h"
+#include "gtk/options.h"
+#include "gtk/dialogs/gtk_options.h"
+#include "utils/container.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+
+struct nsgtk_theme_cache {
+ GdkPixbuf *image[PLACEHOLDER_BUTTON];
+ GdkPixbuf *searchimage[3]; /* back, forward, close */
+ /* apng throbber image */
+};
+
+char *current_theme_name = NULL;
+static struct nsgtk_theme_cache *theme_cache_menu = NULL;
+static struct nsgtk_theme_cache *theme_cache_toolbar = NULL;
+
+static struct nsgtk_theme *nsgtk_theme_default(GtkIconSize s);
+static GtkImage *nsgtk_theme_image_default(int i, GtkIconSize s);
+static bool nsgtk_theme_verify(const char *themename);
+
+#ifdef WITH_THEME_INSTALL
+static struct content *theme_install_content = NULL;
+
+static void theme_install_callback(content_msg msg, struct content *c,
+ intptr_t p1, intptr_t p2, union content_msg_data data);
+static bool theme_install_read(char *data, unsigned long len);
+#endif
+
+void nsgtk_theme_init()
+{
+ if (option_current_theme == 0)
+ return;
+ char *themefile = g_strconcat(res_dir_location, "themelist", NULL);
+ nsgtk_scaffolding *list = scaf_list;
+ nsgtk_theme_verify(NULL);
+ FILE *fp = fopen(themefile, "r");
+ char buf[50];
+ int row_count = 0;
+ if (fp == NULL)
+ return;
+ while (fgets(buf, sizeof(buf), fp)) {
+ if (buf[0] == '\0')
+ continue;
+
+ if (row_count++ == option_current_theme) {
+ if (current_theme_name != NULL)
+ free(current_theme_name);
+ buf[strlen(buf) - 1] = '\0';
+ current_theme_name = strdup(buf);
+ break;
+ }
+ }
+ fclose(fp);
+
+ while (list) {
+ nsgtk_theme_implement(list);
+ list = nsgtk_scaffolding_iterate(list);
+ }
+}
+
+void nsgtk_theme_add(const char *themename)
+{
+ GtkWidget *notification, *label;
+ char *labelcontent, *themefile = g_strconcat(res_dir_location,
+ "themelist", NULL);
+ /* conduct verification here; no adding duplicates to list */
+ if (nsgtk_theme_verify(themename) == false) {
+ warn_user(messages_get("gtkThemeDup"), 0);
+ g_free(themefile);
+ return;
+ }
+ FILE *fp = fopen(themefile, "a");
+ if (fp == NULL) {
+ warn_user(messages_get("gtkFileError"), themefile);
+ g_free(themefile);
+ return;
+ }
+ fprintf(fp, "%s\n", themename);
+ fclose(fp);
+ g_free(themefile);
+
+ /* notification that theme was added successfully */
+ notification = gtk_dialog_new_with_buttons(messages_get("gtkThemeAdd"),
+ NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK,
+ GTK_RESPONSE_NONE, NULL);
+ labelcontent = g_strconcat("\t\t\t", messages_get("gtkThemeAdd"),
+ "\t\t\t", NULL);
+ label = gtk_label_new(labelcontent);
+ g_signal_connect_swapped(notification, "response",
+ G_CALLBACK(gtk_widget_destroy), notification);
+ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(notification)->vbox), label);
+ gtk_widget_show_all(notification);
+ g_free(labelcontent);
+
+ /* update combo */
+ if (wndPreferences == NULL)
+ return;
+ nsgtk_options_combo_theme_add(themename);
+
+}
+
+bool nsgtk_theme_verify(const char *themename)
+{
+ long filelength;
+ FILE *fp;
+ size_t val;
+ char buf[50];
+ char *themefile = g_strconcat(res_dir_location, "themelist", NULL);
+ if (themename == NULL) {
+ char *filecontent, *testfile;
+ struct stat sta;
+ fp = fopen(themefile, "r+");
+ if (fp == NULL) {
+ warn_user(messages_get("gtkFileError"), themefile);
+ g_free(themefile);
+ return true;
+ }
+ fseek(fp, 0L, SEEK_END);
+ filelength = ftell(fp);
+ filecontent = malloc(filelength + 2);
+ strcpy(filecontent, "gtk default theme\n");
+ if (filecontent == NULL) {
+ warn_user(messages_get("NoMemory"), 0);
+ g_free(themefile);
+ return true;
+ }
+ fseek(fp, 0L, SEEK_SET);
+ while (fgets(buf, sizeof(buf), fp)) {
+ /* iterate list */
+ buf[strlen(buf) - 1] = '\0';
+ testfile = g_strconcat(res_dir_location, "themes/",
+ buf, NULL);
+ /* check every directory */
+ if (access(testfile, R_OK) == 0) {
+ stat(testfile, &sta);
+ if (S_ISDIR(sta.st_mode)) {
+ free(testfile);
+ buf[strlen(buf)] = '\n';
+ strcat(filecontent, buf);
+ }
+ }
+ }
+ fclose(fp);
+ fp = fopen(themefile, "w");
+ if (fp == NULL) {
+ warn_user(messages_get("gtkFileError"), themefile);
+ free(filecontent);
+ g_free(themefile);
+ return true;
+ }
+ val = fwrite(filecontent, strlen(filecontent), 1, fp);
+ if (val == 0)
+ LOG(("empty write themelist"));
+ fclose(fp);
+ free(filecontent);
+ g_free(themefile);
+ return true;
+ } else {
+ fp = fopen(themefile, "r");
+ if (fp == NULL) {
+ warn_user(messages_get("gtkFileError"), themefile);
+ g_free(themefile);
+ return false;
+ }
+ while (fgets(buf, sizeof(buf), fp)) {
+ buf[strlen(buf) - 1] = '\0';
+ if (strcmp(buf, themename) == 0) {
+ g_free(themefile);
+ return false;
+ }
+ }
+ fclose(fp);
+ g_free(themefile);
+ return true;
+ }
+
+}
+
+void nsgtk_theme_implement(struct gtk_scaffolding *g)
+{
+ struct nsgtk_theme *theme[4];
+ int i;
+ for (i = 0; i < 3; i++)
+ theme[i] = nsgtk_theme_load(GTK_ICON_SIZE_MENU);
+ theme[3] = nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR);
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ if ((i == URL_BAR_ITEM) || (i == THROBBER_ITEM) ||
+ (i == WEBSEARCH_ITEM))
+ continue;
+ if (nsgtk_scaffolding_button(g, i)->main != NULL) {
+ gtk_image_menu_item_set_image(nsgtk_scaffolding_button(
+ g, i)->main,
+ GTK_WIDGET(theme[0]->image[i]));
+ gtk_widget_show_all(GTK_WIDGET(
+ nsgtk_scaffolding_button(g, i)->main));
+ }
+ if (nsgtk_scaffolding_button(g, i)->rclick != NULL) {
+ gtk_image_menu_item_set_image(nsgtk_scaffolding_button(
+ g, i)->rclick,
+ GTK_WIDGET(theme[1]->image[i]));
+ gtk_widget_show_all(GTK_WIDGET(
+ nsgtk_scaffolding_button(
+ g, i)->rclick));
+ }
+ if (nsgtk_scaffolding_button(g, i)->popup != NULL) {
+ gtk_image_menu_item_set_image(nsgtk_scaffolding_button(
+ g, i)->popup,
+ GTK_WIDGET(theme[2]->image[i]));
+ gtk_widget_show_all(GTK_WIDGET(
+ nsgtk_scaffolding_button(g, i)->
+ popup));
+ }
+ if ((nsgtk_scaffolding_button(g, i)->location != -1) &&
+ (nsgtk_scaffolding_button(g, i)->button
+ != NULL)) {
+ gtk_tool_button_set_icon_widget(
+ GTK_TOOL_BUTTON(
+ nsgtk_scaffolding_button(
+ g, i)->button),
+ GTK_WIDGET(theme[3]->image[i]));
+ gtk_widget_show_all(GTK_WIDGET(
+ nsgtk_scaffolding_button(g, i)->
+ button));
+ }
+ }
+
+ /* set search bar images */
+ gtk_tool_button_set_icon_widget(
+ nsgtk_scaffolding_search(g)->buttons[0],
+ GTK_WIDGET(theme[0]->searchimage[0]));
+ gtk_widget_show_all(GTK_WIDGET(
+ nsgtk_scaffolding_search(g)->buttons[0]));
+ gtk_tool_button_set_icon_widget(
+ nsgtk_scaffolding_search(g)->buttons[1],
+ GTK_WIDGET(theme[0]->searchimage[1]));
+ gtk_widget_show_all(GTK_WIDGET(
+ nsgtk_scaffolding_search(g)->buttons[1]));
+ gtk_tool_button_set_icon_widget(
+ nsgtk_scaffolding_search(g)->buttons[2],
+ GTK_WIDGET(theme[0]->searchimage[2]));
+ gtk_widget_show_all(GTK_WIDGET(
+ nsgtk_scaffolding_search(g)->buttons[2]));
+
+ for (i = 0; i < 4; i++)
+ free(theme[i]);
+}
+
+struct nsgtk_theme *nsgtk_theme_load(GtkIconSize s)
+{
+ if (current_theme_name == NULL)
+ return nsgtk_theme_default(s);
+ struct nsgtk_theme *theme = malloc(sizeof(struct nsgtk_theme));
+ if ((theme_cache_menu == NULL) || (theme_cache_toolbar == NULL))
+ nsgtk_theme_prepare();
+ /* load theme from cache */
+ struct nsgtk_theme_cache *cachetheme = (s == GTK_ICON_SIZE_MENU) ?
+ theme_cache_menu : theme_cache_toolbar;
+#define SET_BUTTON_IMAGE(q)\
+ if (cachetheme->image[q##_BUTTON] != NULL)\
+ theme->image[q##_BUTTON] = GTK_IMAGE(gtk_image_new_from_pixbuf(\
+ cachetheme->image[q##_BUTTON]));\
+ else\
+ theme->image[q##_BUTTON] = nsgtk_theme_image_default(\
+ q##_BUTTON, s);
+ SET_BUTTON_IMAGE(BACK)
+ SET_BUTTON_IMAGE(HISTORY)
+ SET_BUTTON_IMAGE(FORWARD)
+ SET_BUTTON_IMAGE(STOP)
+ SET_BUTTON_IMAGE(RELOAD)
+ SET_BUTTON_IMAGE(HOME)
+ SET_BUTTON_IMAGE(NEWWINDOW)
+ SET_BUTTON_IMAGE(NEWTAB)
+ SET_BUTTON_IMAGE(OPENFILE)
+ SET_BUTTON_IMAGE(CLOSETAB)
+ SET_BUTTON_IMAGE(CLOSEWINDOW)
+ SET_BUTTON_IMAGE(SAVEPAGE)
+ SET_BUTTON_IMAGE(PRINTPREVIEW)
+ SET_BUTTON_IMAGE(PRINT)
+ SET_BUTTON_IMAGE(QUIT)
+ SET_BUTTON_IMAGE(CUT)
+ SET_BUTTON_IMAGE(COPY)
+ SET_BUTTON_IMAGE(PASTE)
+ SET_BUTTON_IMAGE(DELETE)
+ SET_BUTTON_IMAGE(SELECTALL)
+ SET_BUTTON_IMAGE(PREFERENCES)
+ SET_BUTTON_IMAGE(ZOOMPLUS)
+ SET_BUTTON_IMAGE(ZOOMMINUS)
+ SET_BUTTON_IMAGE(ZOOMNORMAL)
+ SET_BUTTON_IMAGE(FULLSCREEN)
+ SET_BUTTON_IMAGE(VIEWSOURCE)
+ SET_BUTTON_IMAGE(CONTENTS)
+ SET_BUTTON_IMAGE(ABOUT)
+ SET_BUTTON_IMAGE(PDF)
+ SET_BUTTON_IMAGE(PLAINTEXT)
+ SET_BUTTON_IMAGE(DRAWFILE)
+ SET_BUTTON_IMAGE(POSTSCRIPT)
+ SET_BUTTON_IMAGE(FIND)
+ SET_BUTTON_IMAGE(DOWNLOADS)
+ SET_BUTTON_IMAGE(SAVEWINDOWSIZE)
+ SET_BUTTON_IMAGE(TOGGLEDEBUGGING)
+ SET_BUTTON_IMAGE(SAVEBOXTREE)
+ SET_BUTTON_IMAGE(SAVEDOMTREE)
+ SET_BUTTON_IMAGE(LOCALHISTORY)
+ SET_BUTTON_IMAGE(GLOBALHISTORY)
+ SET_BUTTON_IMAGE(ADDBOOKMARKS)
+ SET_BUTTON_IMAGE(SHOWBOOKMARKS)
+ SET_BUTTON_IMAGE(OPENLOCATION)
+ SET_BUTTON_IMAGE(NEXTTAB)
+ SET_BUTTON_IMAGE(PREVTAB)
+ SET_BUTTON_IMAGE(GUIDE)
+ SET_BUTTON_IMAGE(INFO)
+#undef SET_BUTTON_IMAGE
+#define SET_BUTTON_IMAGE(p, q)\
+ if (cachetheme->searchimage[p] != NULL)\
+ theme->searchimage[p] = GTK_IMAGE(gtk_image_new_from_pixbuf(\
+ cachetheme->searchimage[p]));\
+ else if (cachetheme->image[q##_BUTTON] != NULL)\
+ theme->searchimage[p] = GTK_IMAGE(gtk_image_new_from_pixbuf(\
+ cachetheme->image[q##_BUTTON]));\
+ else\
+ theme->searchimage[p] = nsgtk_theme_image_default(\
+ PLACEHOLDER_BUTTON + p, s);
+ SET_BUTTON_IMAGE(0, BACK)
+ SET_BUTTON_IMAGE(1, FORWARD)
+ SET_BUTTON_IMAGE(2, CLOSEWINDOW)
+#undef SET_BUTTON_IMAGE
+ return theme;
+}
+
+void nsgtk_theme_prepare(void)
+{
+ if (current_theme_name == NULL)
+ return;
+ if (theme_cache_menu == NULL)
+ theme_cache_menu = malloc(sizeof(struct nsgtk_theme_cache));
+ if (theme_cache_toolbar == NULL)
+ theme_cache_toolbar = malloc(sizeof(struct nsgtk_theme_cache));
+ char *path = g_strconcat(res_dir_location, "themes/",
+ current_theme_name, "/", NULL);
+ char *filename;
+#define CACHE_IMAGE(p, q)\
+ filename = g_strconcat(path, #q, ".png", NULL);\
+ theme_cache_toolbar->image[p##_BUTTON] =\
+ gdk_pixbuf_new_from_file_at_size(filename, 24, 24,\
+ NULL);\
+ theme_cache_menu->image[p##_BUTTON] =\
+ gdk_pixbuf_new_from_file_at_size(filename, 16, 16,\
+ NULL);\
+ g_free(filename)
+ CACHE_IMAGE(BACK, back);
+ CACHE_IMAGE(HISTORY, history);
+ CACHE_IMAGE(FORWARD, forward);
+ CACHE_IMAGE(STOP, stop);
+ CACHE_IMAGE(RELOAD, reload);
+ CACHE_IMAGE(HOME, home);
+ CACHE_IMAGE(NEWWINDOW, newwindow);
+ CACHE_IMAGE(NEWTAB, newtab);
+ CACHE_IMAGE(OPENFILE, openfile);
+ CACHE_IMAGE(CLOSETAB, closetab);
+ CACHE_IMAGE(CLOSEWINDOW, closewindow);
+ CACHE_IMAGE(SAVEPAGE, savepage);
+ CACHE_IMAGE(PRINTPREVIEW, printpreview);
+ CACHE_IMAGE(PRINT, print);
+ CACHE_IMAGE(QUIT, quit);
+ CACHE_IMAGE(CUT, cut);
+ CACHE_IMAGE(COPY, copy);
+ CACHE_IMAGE(PASTE, paste);
+ CACHE_IMAGE(DELETE, delete);
+ CACHE_IMAGE(SELECTALL, selectall);
+ CACHE_IMAGE(PREFERENCES, preferences);
+ CACHE_IMAGE(ZOOMPLUS, zoomplus);
+ CACHE_IMAGE(ZOOMMINUS, zoomminus);
+ CACHE_IMAGE(ZOOMNORMAL, zoomnormal);
+ CACHE_IMAGE(FULLSCREEN, fullscreen);
+ CACHE_IMAGE(VIEWSOURCE, viewsource);
+ CACHE_IMAGE(CONTENTS, helpcontents);
+ CACHE_IMAGE(ABOUT, helpabout);
+ CACHE_IMAGE(PDF, pdf);
+ CACHE_IMAGE(PLAINTEXT, plaintext);
+ CACHE_IMAGE(DRAWFILE, drawfile);
+ CACHE_IMAGE(POSTSCRIPT, postscript);
+ CACHE_IMAGE(FIND, find);
+ CACHE_IMAGE(DOWNLOADS, downloads);
+ CACHE_IMAGE(SAVEWINDOWSIZE, savewindowsize);
+ CACHE_IMAGE(TOGGLEDEBUGGING, toggledebugging);
+ CACHE_IMAGE(SAVEBOXTREE, boxtree);
+ CACHE_IMAGE(SAVEDOMTREE, domtree);
+ CACHE_IMAGE(LOCALHISTORY, localhistory);
+ CACHE_IMAGE(GLOBALHISTORY, globalhistory);
+ CACHE_IMAGE(ADDBOOKMARKS, addbookmarks);
+ CACHE_IMAGE(SHOWBOOKMARKS, showbookmarks);
+ CACHE_IMAGE(OPENLOCATION, openlocation);
+ CACHE_IMAGE(NEXTTAB, nexttab);
+ CACHE_IMAGE(PREVTAB, prevtab);
+ CACHE_IMAGE(GUIDE, helpguide);
+ CACHE_IMAGE(INFO, helpinfo);
+#undef CACHE_IMAGE
+#define CACHE_IMAGE(p, q)\
+ filename = g_strconcat(path, #q, ".png", NULL);\
+ theme_cache_toolbar->searchimage[p] =\
+ gdk_pixbuf_new_from_file_at_size(filename, 24, 24,\
+ NULL);\
+ theme_cache_menu->searchimage[p] =\
+ gdk_pixbuf_new_from_file_at_size(filename, 16, 16,\
+ NULL);\
+ g_free(filename)
+ CACHE_IMAGE(0, searchback);
+ CACHE_IMAGE(1, searchforward);
+ CACHE_IMAGE(2, searchclose);
+#undef CACHE_IMAGE
+ g_free(path);
+}
+
+GtkImage *nsgtk_theme_image_default(int i, GtkIconSize s)
+{
+ char *imagefile;
+ GtkImage *image;
+ switch(i) {
+#define BUTTON_IMAGE(p, q)\
+ case p##_BUTTON:\
+ return GTK_IMAGE(gtk_image_new_from_stock(#q, s))
+ BUTTON_IMAGE(BACK, gtk-go-back);
+ case HISTORY_BUTTON:
+ imagefile = g_strconcat(res_dir_location,
+ "arrow_down_8x32.png", NULL);
+ image = GTK_IMAGE(gtk_image_new_from_file(imagefile));
+ g_free(imagefile);
+ return image;
+ BUTTON_IMAGE(FORWARD, gtk-go-forward);
+ BUTTON_IMAGE(STOP, gtk-stop);
+ BUTTON_IMAGE(RELOAD, gtk-refresh);
+ BUTTON_IMAGE(HOME, gtk-home);
+ BUTTON_IMAGE(NEWWINDOW, gtk-new);
+ BUTTON_IMAGE(NEWTAB, gtk-new);
+ BUTTON_IMAGE(OPENFILE, gtk-open);
+ BUTTON_IMAGE(CLOSETAB, gtk-close);
+ BUTTON_IMAGE(CLOSEWINDOW, gtk-close);
+ BUTTON_IMAGE(SAVEPAGE, gtk-save-as);
+ BUTTON_IMAGE(PRINTPREVIEW, gtk-print-preview);
+ BUTTON_IMAGE(PRINT, gtk-print);
+ BUTTON_IMAGE(QUIT, gtk-quit);
+ BUTTON_IMAGE(CUT, gtk-cut);
+ BUTTON_IMAGE(COPY, gtk-copy);
+ BUTTON_IMAGE(PASTE, gtk-paste);
+ BUTTON_IMAGE(DELETE, gtk-delete);
+ BUTTON_IMAGE(SELECTALL, gtk-select-all);
+ BUTTON_IMAGE(FIND, gtk-find);
+ BUTTON_IMAGE(PREFERENCES, gtk-preferences);
+ BUTTON_IMAGE(ZOOMPLUS, gtk-zoom-in);
+ BUTTON_IMAGE(ZOOMMINUS, gtk-zoom-out);
+ BUTTON_IMAGE(ZOOMNORMAL, gtk-zoom-100);
+ BUTTON_IMAGE(FULLSCREEN, gtk-fullscreen);
+ BUTTON_IMAGE(VIEWSOURCE, gtk-index);
+ BUTTON_IMAGE(CONTENTS, gtk-help);
+ BUTTON_IMAGE(ABOUT, gtk-about);
+#undef BUTTON_IMAGE
+ case (PLACEHOLDER_BUTTON):
+ return GTK_IMAGE(gtk_image_new_from_stock("gtk-go-back", s));
+ case (PLACEHOLDER_BUTTON + 1):
+ return GTK_IMAGE(gtk_image_new_from_stock("gtk-go-forward",
+ s));
+ case (PLACEHOLDER_BUTTON + 2):
+ return GTK_IMAGE(gtk_image_new_from_stock("gtk-close", s));
+ default:
+ imagefile = g_strconcat(res_dir_location, "themes/Alpha.png",
+ NULL);
+ image = GTK_IMAGE(gtk_image_new_from_file(imagefile));
+ g_free(imagefile);
+ return image;
+ }
+}
+
+
+#ifdef WITH_THEME_INSTALL
+/**
+ * Handle CONTENT_THEME
+ */
+void theme_install_start(struct content *c)
+{
+ assert(c);
+ assert(c->type == CONTENT_THEME);
+
+ /* stop theme sitting in memory cache */
+ c->fresh = false;
+ if (!content_add_user(c, theme_install_callback, 0, 0)) {
+ warn_user("NoMemory", 0);
+ return;
+ }
+}
+
+
+/**
+ * Callback for fetchcache() for theme install fetches.
+ */
+void theme_install_callback(content_msg msg, struct content *c,
+ intptr_t p1, intptr_t p2, union content_msg_data data)
+{
+ switch (msg) {
+ case CONTENT_MSG_READY:
+ break;
+
+ case CONTENT_MSG_DONE:
+ theme_install_content = c;
+ if (!theme_install_read(c->source_data, c->source_size))
+ warn_user("ThemeInvalid", 0);
+ break;
+
+ case CONTENT_MSG_ERROR:
+ warn_user(data.error, 0);
+ break;
+
+ case CONTENT_MSG_STATUS:
+ break;
+
+ case CONTENT_MSG_LOADING:
+ case CONTENT_MSG_REFORMAT:
+ case CONTENT_MSG_REDRAW:
+ case CONTENT_MSG_NEWPTR:
+ case CONTENT_MSG_LAUNCH:
+ case CONTENT_MSG_AUTH:
+ default:
+ assert(0);
+ break;
+ }
+}
+
+/**
+ * handler saves theme data as a local theme
+ */
+bool theme_install_read(char *data, unsigned long len)
+{
+ char *filename;
+ int handle = g_file_open_tmp("nsgtkthemeXXXXXX", &filename, NULL);
+ ssize_t written = write(handle, data, len);
+ close(handle);
+ if ((unsigned)written != len)
+ return false;
+
+ /* get name of theme; set as dirname */
+ char *dirname = g_strconcat(res_dir_location, "themes/", NULL);
+ if (dirname == NULL)
+ return false;
+
+ /* save individual files in theme */
+ filename = container_extract_theme(filename, dirname);
+ g_free(dirname);
+ if (filename == NULL)
+ return false;
+ nsgtk_theme_add(filename);
+
+ return true;
+}
+#endif
+
+struct nsgtk_theme *nsgtk_theme_default(GtkIconSize s)
+{
+ struct nsgtk_theme *theme = malloc(sizeof(struct nsgtk_theme));
+ for (int i = BACK_BUTTON; i <= PLACEHOLDER_BUTTON + 2; i++)
+ theme->image[i] = nsgtk_theme_image_default(i, s);
+ return theme;
+}
+
Index: gtk/gtk_toolbar.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ gtk/gtk_toolbar.c 2009-07-10 12:49:36.000000000 +0100
@@ -0,0 +1,957 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin(a)dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/ >.
+ */
+
+#include <gtk/gtk.h>
+#include "gtk/gtk_toolbar.h"
+#include "gtk/gtk_gui.h"
+#include "gtk/gtk_scaffolding.h"
+#include "gtk/gtk_search.h"
+#include "gtk/gtk_theme.h"
+#include "gtk/gtk_throbber.h"
+#include "gtk/gtk_window.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+
+static GtkTargetEntry entry = {(char *)"nsgtk_button_data",
+ GTK_TARGET_SAME_APP, 0};
+
+static bool edit_mode = false;
+
+struct nsgtk_toolbar_custom_store {
+ GtkWidget *window;
+/* currently includes all menu items that have stock images that could be
+ * added to buttons; once theming is added, could allow all menu items;
+ * until then, need to consider adding images to important menu items such
+ * as savetext, savepdf, find, downloads, debugrendering, localhistory,
+ * globalhistory, addbookmarks, editbookmarks */
+ GtkWidget *store_buttons[PLACEHOLDER_BUTTON];
+ GtkWidget *widgetvbox;
+ GtkWidget *currentbar;
+ char numberh;
+ GladeXML *glade;
+ int buttonlocations[PLACEHOLDER_BUTTON];
+ int currentbutton;
+ bool fromstore;
+};
+
+static struct nsgtk_toolbar_custom_store store;
+static struct nsgtk_toolbar_custom_store *window = &store;
+
+static void nsgtk_toolbar_close(struct gtk_scaffolding *g);
+static void nsgtk_toolbar_window_open(struct gtk_scaffolding *g);
+static void nsgtk_toolbar_customization_save(struct gtk_scaffolding *g);
+static void nsgtk_toolbar_add_item_to_toolbar(struct gtk_scaffolding *g, int i,
+ struct nsgtk_theme *theme);
+static bool nsgtk_toolbar_add_store_widget(GtkWidget *widget);
+static gboolean nsgtk_toolbar_data(GtkWidget *widget, GdkDragContext *context,
+ gint x, gint y, guint time, gpointer data);
+static gboolean nsgtk_toolbar_store_return(GtkWidget *widget, GdkDragContext *gdc, gint x, gint y, guint time, gpointer data);
+static gboolean nsgtk_toolbar_action(GtkWidget *widget, GdkDragContext
+ *drag_context, gint x, gint y, guint time, gpointer data);
+gboolean nsgtk_toolbar_store_action(GtkWidget *widget, GdkDragContext *gdc,
+ gint x, gint y, guint time, gpointer data);
+static gboolean nsgtk_toolbar_move_complete(GtkWidget *widget, GdkDragContext
+ *gdc, gint x, gint y, GtkSelectionData *selection, guint info,
+ guint time, gpointer data);
+static void nsgtk_toolbar_clear(GtkWidget *widget, GdkDragContext *gdc, guint
+ time, gpointer data);
+static gboolean nsgtk_toolbar_delete(GtkWidget *widget, GdkEvent *event,
+ gpointer data);
+static gboolean nsgtk_toolbar_cancel_clicked(GtkWidget *widget, gpointer data);
+static gboolean nsgtk_toolbar_reset(GtkWidget *widget, gpointer data);
+static gboolean nsgtk_toolbar_persist(GtkWidget *widget, gpointer data);
+static void nsgtk_toolbar_cast(struct gtk_scaffolding *g);
+static GtkWidget *nsgtk_toolbar_make_widget(struct gtk_scaffolding *g, int i,
+ struct nsgtk_theme *theme);
+static void nsgtk_toolbar_set_handler(struct gtk_scaffolding *g, int i);
+static void nsgtk_toolbar_temp_connect(struct gtk_scaffolding *g, int i);
+static void nsgtk_toolbar_clear_toolbar(GtkWidget *widget, gpointer data);
+static int nsgtk_toolbar_get_id_at_location(struct gtk_scaffolding *g, int i);
+
+/**
+ * change behaviour of scaffoldings while editing toolbar
+ */
+void nsgtk_toolbar_customization_init(struct gtk_scaffolding *g)
+{
+ int i;
+ nsgtk_scaffolding *list = scaf_list;;
+ edit_mode = true;
+
+ while (list) {
+ g_signal_handler_block(GTK_WIDGET(
+ nsgtk_window_get_drawing_area(
+ nsgtk_scaffolding_top_level(list))),
+ nsgtk_window_get_signalhandler(
+ nsgtk_scaffolding_top_level(list), 0));
+ g_signal_handler_block(GTK_WIDGET(
+ nsgtk_window_get_drawing_area(
+ nsgtk_scaffolding_top_level(list))),
+ nsgtk_window_get_signalhandler(
+ nsgtk_scaffolding_top_level(list), 1));
+ gtk_widget_modify_bg(GTK_WIDGET(nsgtk_window_get_drawing_area(
+ nsgtk_scaffolding_top_level(list))),
+ GTK_STATE_NORMAL, &((GdkColor)
+ {0, 0xEEEE, 0xEEEE, 0xEEEE}));
+
+ if (list == g) {
+ list = nsgtk_scaffolding_iterate(list);
+ continue;
+ }
+ /* set sensitive for all gui_windows save g */
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_window(
+ list)), FALSE);
+ list = nsgtk_scaffolding_iterate(list);
+ }
+ /* set sensitive for all of g save toolbar */
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_menu_bar(g)),
+ FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_notebook(g)),
+ FALSE);
+
+ /* set editable aspect for toolbar */
+ gtk_container_foreach(GTK_CONTAINER(nsgtk_scaffolding_toolbar(g)),
+ nsgtk_toolbar_clear_toolbar, g);
+ nsgtk_toolbar_set_physical(g);
+ /* memorize button locations, set editable */
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ window->buttonlocations[i] = nsgtk_scaffolding_button(g, i)
+ ->location;
+ if ((window->buttonlocations[i] == -1) || (i == URL_BAR_ITEM))
+ continue;
+ gtk_tool_item_set_use_drag_window(GTK_TOOL_ITEM(
+ nsgtk_scaffolding_button(g, i)->button), TRUE);
+ gtk_drag_source_set(GTK_WIDGET(nsgtk_scaffolding_button(
+ g, i)->button), GDK_BUTTON1_MASK, &entry, 1,
+ GDK_ACTION_COPY);
+ nsgtk_toolbar_temp_connect(g, i);
+ }
+
+ /* add move button listeners */
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)),
+ "drag-drop", G_CALLBACK(nsgtk_toolbar_data), g);
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)),
+ "drag-data-received", G_CALLBACK(
+ nsgtk_toolbar_move_complete), g);
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)),
+ "drag-motion", G_CALLBACK(nsgtk_toolbar_action), g);
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)),
+ "drag-leave", G_CALLBACK(
+ nsgtk_toolbar_clear), g);
+
+ /* set data types */
+ gtk_drag_dest_set(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)),
+ GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
+ &entry, 1, GDK_ACTION_COPY);
+
+ /* open toolbar window */
+ nsgtk_toolbar_window_open(g);
+}
+
+/**
+ * create store window
+ */
+void nsgtk_toolbar_window_open(struct gtk_scaffolding *g)
+{
+ int x,y;
+ struct nsgtk_theme *theme =
+ nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR);
+ window->glade = glade_xml_new(glade_toolbar_file_location,
+ "toolbarwindow", NULL);
+ glade_xml_signal_autoconnect(window->glade);
+
+#define GET_TOOLWIDGET(p, q) window->p = glade_xml_get_widget(window->glade,\
+ #q)
+ GET_TOOLWIDGET(window, toolbarwindow);
+ GET_TOOLWIDGET(widgetvbox, widgetvbox);
+#undef GET_TOOLWIDGET
+
+ window->numberh = 6;
+ window->currentbutton = -1;
+ /* load toolbuttons */
+ /* add toolbuttons to window */
+ /* set event handlers */
+ for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ if (i == URL_BAR_ITEM)
+ continue;
+ window->store_buttons[i] =
+ nsgtk_toolbar_make_widget(g, i, theme);
+ nsgtk_toolbar_add_store_widget(window->store_buttons[i]);
+ g_signal_connect(window->store_buttons[i], "drag-data-get",
+ G_CALLBACK(
+ nsgtk_scaffolding_button(g, i)->dataplus), g);
+ }
+ free(theme);
+ gtk_window_set_transient_for(GTK_WINDOW(window->window),
+ nsgtk_scaffolding_window(g));
+ gtk_window_set_title(GTK_WINDOW(window->window), messages_get(
+ "gtkToolBarTitle"));
+ gtk_window_set_accept_focus(GTK_WINDOW(window->window), FALSE);
+ gtk_drag_dest_set(GTK_WIDGET(window->window), GTK_DEST_DEFAULT_MOTION |
+ GTK_DEST_DEFAULT_DROP, &entry, 1, GDK_ACTION_COPY);
+ gtk_widget_show_all(window->window);
+ gtk_window_set_position(GTK_WINDOW(window->window),
+ GTK_WIN_POS_CENTER_ON_PARENT);
+ gtk_window_get_position(nsgtk_scaffolding_window(g), &x, &y);
+ gtk_window_move(GTK_WINDOW(window->window), x, y + 100);
+ g_signal_connect(glade_xml_get_widget(window->glade, "cancelbutton"),
+ "clicked", G_CALLBACK(
+ nsgtk_toolbar_cancel_clicked), g);
+ g_signal_connect(glade_xml_get_widget(window->glade, "okbutton"),
+ "clicked", G_CALLBACK(nsgtk_toolbar_persist), g);
+ g_signal_connect(glade_xml_get_widget(window->glade, "resetbutton"),
+ "clicked", G_CALLBACK(nsgtk_toolbar_reset), g);
+ g_signal_connect(window->window, "delete-event",
+ G_CALLBACK(nsgtk_toolbar_delete), g);
+ g_signal_connect(window->window, "drag-drop",
+ G_CALLBACK(nsgtk_toolbar_store_return), g);
+ g_signal_connect(window->window, "drag-motion",
+ G_CALLBACK(nsgtk_toolbar_store_action), g);
+}
+
+/**
+ * when titlebar / alt-F4 window close event happens
+ */
+gboolean nsgtk_toolbar_delete(GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ edit_mode = false;
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
+ /* reset g->buttons->location */
+ for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ nsgtk_scaffolding_button(g, i)->location =
+ window->buttonlocations[i];
+ }
+ nsgtk_toolbar_set_physical(g);
+ nsgtk_toolbar_connect_all(g);
+ nsgtk_toolbar_close(g);
+ nsgtk_scaffolding_set_sensitivity(g);
+ gtk_widget_destroy(window->window);
+ return TRUE;
+}
+
+/**
+ * when cancel button is clicked
+ */
+gboolean nsgtk_toolbar_cancel_clicked(GtkWidget *widget, gpointer data)
+{
+ edit_mode = false;
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
+ /* reset g->buttons->location */
+ for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ nsgtk_scaffolding_button(g, i)->location =
+ window->buttonlocations[i];
+ }
+ nsgtk_toolbar_set_physical(g);
+ nsgtk_toolbar_connect_all(g);
+ nsgtk_toolbar_close(g);
+ nsgtk_scaffolding_set_sensitivity(g);
+ gtk_widget_destroy(window->window);
+ return TRUE;
+}
+
+/**
+ * when 'save settings' button is clicked
+ */
+gboolean nsgtk_toolbar_persist(GtkWidget *widget, gpointer data)
+{
+ edit_mode = false;
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
+ /* save state to file, update toolbars for all windows */
+ nsgtk_toolbar_customization_save(g);
+ nsgtk_toolbar_cast(g);
+ nsgtk_toolbar_set_physical(g);
+ nsgtk_toolbar_close(g);
+ gtk_widget_destroy(window->window);
+ return TRUE;
+}
+
+/**
+ * when 'reload defaults' button is clicked
+ */
+gboolean nsgtk_toolbar_reset(GtkWidget *widget, gpointer data)
+{
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
+ int i;
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++)
+ nsgtk_scaffolding_button(g, i)->location =
+ (i <= WEBSEARCH_ITEM) ? i : -1;
+ nsgtk_toolbar_set_physical(g);
+ for (i = BACK_BUTTON; i <= WEBSEARCH_ITEM; i++) {
+ if (i == URL_BAR_ITEM)
+ continue;
+ gtk_tool_item_set_use_drag_window(GTK_TOOL_ITEM(
+ nsgtk_scaffolding_button(g, i)->button), TRUE);
+ gtk_drag_source_set(GTK_WIDGET(
+ nsgtk_scaffolding_button(g, i)->button),
+ GDK_BUTTON1_MASK, &entry, 1, GDK_ACTION_COPY);
+ nsgtk_toolbar_temp_connect(g, i);
+ }
+ return TRUE;
+}
+
+/**
+ * set toolbar logical -> physical
+ */
+void nsgtk_toolbar_set_physical(struct gtk_scaffolding *g)
+{
+ int i;
+ struct nsgtk_theme *theme =
+ nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR);
+ /* simplest is to clear the toolbar then reload it from memory */
+ gtk_container_foreach(GTK_CONTAINER(nsgtk_scaffolding_toolbar(g)),
+ nsgtk_toolbar_clear_toolbar, g);
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++)
+ nsgtk_toolbar_add_item_to_toolbar(g, i, theme);
+ gtk_widget_show_all(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)));
+ free(theme);
+}
+
+/**
+ * physical update of all toolbars; resensitize
+ * \param g the 'front' scaffolding that called customize
+ */
+void nsgtk_toolbar_close(struct gtk_scaffolding *g)
+{
+ int i;
+ nsgtk_scaffolding *list = scaf_list;
+ while (list) {
+ struct nsgtk_theme *theme =
+ nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR);
+ /* clear toolbar */
+ gtk_container_foreach(GTK_CONTAINER(nsgtk_scaffolding_toolbar(
+ list)), nsgtk_toolbar_clear_toolbar, list);
+ /* then add items */
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ nsgtk_toolbar_add_item_to_toolbar(list, i, theme);
+ }
+ nsgtk_toolbar_connect_all(list);
+ gtk_widget_show_all(GTK_WIDGET(nsgtk_scaffolding_toolbar(
+ list)));
+ nsgtk_scaffolding_set_sensitivity(list);
+ gtk_widget_modify_bg(GTK_WIDGET(nsgtk_window_get_drawing_area(
+ nsgtk_scaffolding_top_level(list))),
+ GTK_STATE_NORMAL, &((GdkColor)
+ {0, 0xFFFF, 0xFFFF, 0xFFFF}));
+ g_signal_handler_unblock(GTK_WIDGET(
+ nsgtk_window_get_drawing_area(
+ nsgtk_scaffolding_top_level(list))),
+ nsgtk_window_get_signalhandler(
+ nsgtk_scaffolding_top_level(list), 0));
+ g_signal_handler_unblock(GTK_WIDGET(
+ nsgtk_window_get_drawing_area(
+ nsgtk_scaffolding_top_level(list))),
+ nsgtk_window_get_signalhandler(
+ nsgtk_scaffolding_top_level(list), 1));
+ if ((gui_window_get_browser_window(nsgtk_scaffolding_top_level(
+ list))->current_content != NULL) &&
+ (gui_window_get_browser_window(
+ nsgtk_scaffolding_top_level(list))->
+ current_content->url != NULL))
+ browser_window_refresh_url_bar(
+ gui_window_get_browser_window(
+ nsgtk_scaffolding_top_level(list)),
+ gui_window_get_browser_window(
+ nsgtk_scaffolding_top_level(list))->
+ current_content->url,
+ gui_window_get_browser_window(
+ nsgtk_scaffolding_top_level(list))->
+ frag_id);
+
+ if (list != g)
+ gtk_widget_set_sensitive(GTK_WIDGET(
+ nsgtk_scaffolding_window(list)), TRUE);
+ free(theme);
+ list = nsgtk_scaffolding_iterate(list);
+ }
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_notebook(g)),
+ TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_menu_bar(g)),
+ TRUE);
+ gui_window_set_search_ico();
+}
+
+/**
+ * callback function to iterate toolbar's widgets
+ */
+void nsgtk_toolbar_clear_toolbar(GtkWidget *widget, gpointer data)
+{
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
+ gtk_container_remove(GTK_CONTAINER(nsgtk_scaffolding_toolbar(g)), widget);
+}
+
+/**
+ * add item to toolbar
+ * \param g the scaffolding whose toolbar an item is added to
+ * \param i the location in the toolbar
+ * the function should be called, when multiple items are being added,
+ * in ascending order
+ */
+void nsgtk_toolbar_add_item_to_toolbar(struct gtk_scaffolding *g, int i,
+ struct nsgtk_theme *theme)
+{
+ int q;
+ for (q = BACK_BUTTON; q < PLACEHOLDER_BUTTON; q++)
+ if (nsgtk_scaffolding_button(g, q)->location == i) {
+ nsgtk_scaffolding_button(g, q)->button = GTK_TOOL_ITEM(
+ nsgtk_toolbar_make_widget(g, q,
+ theme));
+ gtk_toolbar_insert(nsgtk_scaffolding_toolbar(g),
+ nsgtk_scaffolding_button(g, q)->button,
+ i);
+ break;
+ }
+}
+
+/**
+ * physically add widgets to store window
+ */
+bool nsgtk_toolbar_add_store_widget(GtkWidget *widget)
+{
+ if (window->numberh >= 6) {
+ window->currentbar = gtk_toolbar_new();
+ gtk_toolbar_set_style(GTK_TOOLBAR(window->currentbar),
+ GTK_TOOLBAR_BOTH);
+ gtk_toolbar_set_icon_size(GTK_TOOLBAR(window->currentbar),
+ GTK_ICON_SIZE_LARGE_TOOLBAR);
+ gtk_box_pack_start(GTK_BOX(window->widgetvbox),
+ window->currentbar, FALSE, FALSE, 0);
+ window->numberh = 0;
+ }
+ gtk_widget_set_size_request(widget, 111, 70);
+ gtk_toolbar_insert(GTK_TOOLBAR(window->currentbar), GTK_TOOL_ITEM(
+ widget), window->numberh++);
+ gtk_tool_item_set_use_drag_window(GTK_TOOL_ITEM(widget), TRUE);
+ gtk_drag_source_set(widget, GDK_BUTTON1_MASK, &entry, 1,
+ GDK_ACTION_COPY);
+ gtk_widget_show_all(window->window);
+ return true;
+}
+
+/**
+ * called when a widget is dropped onto the toolbar
+ */
+gboolean nsgtk_toolbar_data(GtkWidget *widget, GdkDragContext *gdc, gint x,
+ gint y, guint time, gpointer data)
+{
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
+ int ind = gtk_toolbar_get_drop_index(nsgtk_scaffolding_toolbar(g),
+ x, y);
+ int q, i;
+ if (window->currentbutton == -1)
+ return TRUE;
+ struct nsgtk_theme *theme =
+ nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR);
+ if (nsgtk_scaffolding_button(g, window->currentbutton)->location
+ != -1) {
+ if (nsgtk_scaffolding_button(g, window->currentbutton)->
+ location < ind)
+ ind--;
+ gtk_container_remove(GTK_CONTAINER(
+ nsgtk_scaffolding_toolbar(g)), GTK_WIDGET(
+ nsgtk_scaffolding_button(g,
+ window->currentbutton)->button));
+ for (i = nsgtk_scaffolding_button(g, window->currentbutton)->
+ location + 1; i < PLACEHOLDER_BUTTON; i++) {
+ q = nsgtk_toolbar_get_id_at_location(g, i);
+ if (q == -1)
+ continue;
+ nsgtk_scaffolding_button(g, q)->location--;
+ }
+ nsgtk_scaffolding_button(g, window->currentbutton)->
+ location = -1;
+ }
+ nsgtk_scaffolding_button(g, window->currentbutton)->button =
+ GTK_TOOL_ITEM(nsgtk_toolbar_make_widget(g,
+ window->currentbutton, theme));
+ free(theme);
+ /* update logical schema */
+ nsgtk_scaffolding_reset_offset(g);
+ for (i = PLACEHOLDER_BUTTON - 1; i >= ind; i--) {
+ q = nsgtk_toolbar_get_id_at_location(g, i);
+ if (q == -1)
+ continue;
+ nsgtk_scaffolding_button(g, q)->location++;
+ }
+ nsgtk_scaffolding_button(g, window->currentbutton)->location = ind;
+
+ /* complete action */
+ gtk_toolbar_insert(nsgtk_scaffolding_toolbar(g),
+ nsgtk_scaffolding_button(g,
+ window->currentbutton)->button, ind);
+ gtk_tool_item_set_use_drag_window(GTK_TOOL_ITEM(
+ nsgtk_scaffolding_button(g,
+ window->currentbutton)->button), TRUE);
+ gtk_drag_source_set(GTK_WIDGET(
+ nsgtk_scaffolding_button(g,
+ window->currentbutton)->button),
+ GDK_BUTTON1_MASK, &entry, 1, GDK_ACTION_COPY);
+ nsgtk_toolbar_temp_connect(g, window->currentbutton);
+ gtk_widget_show_all(GTK_WIDGET(
+ nsgtk_scaffolding_button(g,
+ window->currentbutton)->button));
+ window->currentbutton = -1;
+ return TRUE;
+}
+
+gboolean nsgtk_toolbar_move_complete(GtkWidget *widget, GdkDragContext *gdc,
+ gint x, gint y, GtkSelectionData *selection, guint info, guint
+ time, gpointer data)
+{
+ return FALSE;
+}
+/**
+ * called when a widget is dropped onto the store window
+ */
+gboolean nsgtk_toolbar_store_return(GtkWidget *widget, GdkDragContext *gdc,
+ gint x, gint y, guint time, gpointer data)
+{
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
+ int q, i;
+
+ if ((window->fromstore) || (window->currentbutton == -1)) {
+ window->currentbutton = -1;
+ return FALSE;
+ }
+ if (nsgtk_scaffolding_button(g, window->currentbutton)->location
+ != -1) {
+ for (i = nsgtk_scaffolding_button(g, window->currentbutton)->
+ location + 1; i < PLACEHOLDER_BUTTON; i++) {
+ q = nsgtk_toolbar_get_id_at_location(g, i);
+ if (q == -1)
+ continue;
+ nsgtk_scaffolding_button(g, q)->location--;
+ }
+ gtk_container_remove(GTK_CONTAINER(
+ nsgtk_scaffolding_toolbar(g)), GTK_WIDGET(
+ nsgtk_scaffolding_button(g,
+ window->currentbutton)->button));
+ nsgtk_scaffolding_button(g, window->currentbutton)->location
+ = -1;
+ }
+ window->currentbutton = -1;
+ gtk_drag_finish(gdc, TRUE, TRUE, time);
+ return FALSE;
+}
+/**
+ * called when hovering an item above the toolbar
+ */
+gboolean nsgtk_toolbar_action(GtkWidget *widget, GdkDragContext *gdc, gint x,
+ gint y, guint time, gpointer data)
+{
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
+ GtkToolItem *item = gtk_tool_button_new(NULL, NULL);
+ gtk_toolbar_set_drop_highlight_item(nsgtk_scaffolding_toolbar(g),
+ GTK_TOOL_ITEM(item), gtk_toolbar_get_drop_index(
+ nsgtk_scaffolding_toolbar(g), x, y));
+ return FALSE;
+}
+
+/**
+ * called when hovering above the store
+ */
+gboolean nsgtk_toolbar_store_action(GtkWidget *widget, GdkDragContext *gdc,
+ gint x, gint y, guint time, gpointer data)
+{
+ return FALSE;
+}
+/**
+ * called when hovering stops
+ */
+void nsgtk_toolbar_clear(GtkWidget *widget, GdkDragContext *gdc, guint time,
+ gpointer data)
+{
+ gtk_toolbar_set_drop_highlight_item(GTK_TOOLBAR(widget), NULL, 0);
+}
+
+/**
+ * widget factory for creation of toolbar item widgets
+ * \param g the reference scaffolding
+ * \param i the id of the widget
+ * \param theme the theme to make the widgets from
+ */
+GtkWidget *nsgtk_toolbar_make_widget(struct gtk_scaffolding *g, int i,
+ struct nsgtk_theme *theme)
+{
+ GtkWidget *w = NULL, *image = NULL, *hbox = NULL, *entry = NULL;
+ char *label;
+ GtkStockItem item;
+ switch(i){
+#define MAKE_STOCKBUTTON(p, q) case p##_BUTTON:\
+ gtk_stock_lookup(#q, &item);\
+ label = remove_underscores(item.label, false);\
+ w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET(\
+ theme->image[p##_BUTTON]),label));\
+ free(label);\
+ break
+ MAKE_STOCKBUTTON(HOME, gtk-home);
+ MAKE_STOCKBUTTON(BACK, gtk-go-back);
+ MAKE_STOCKBUTTON(FORWARD, gtk-go-forward);
+ MAKE_STOCKBUTTON(STOP, gtk-stop);
+ MAKE_STOCKBUTTON(RELOAD, gtk-refresh);
+#undef MAKE_STOCKBUTTON
+ case HISTORY_BUTTON:
+ w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET(
+ theme->image[HISTORY_BUTTON]), NULL));
+ break;
+ case URL_BAR_ITEM:
+ label = g_strconcat(res_dir_location, "netsurf-16x16.xpm", NULL);
+ hbox = gtk_hbox_new(FALSE, 0);
+ image = GTK_WIDGET(gtk_image_new_from_file(label));
+ g_free(label);
+ entry = GTK_WIDGET(gtk_entry_new());
+ gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
+ gtk_box_pack_end(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
+ w = GTK_WIDGET(gtk_tool_item_new());
+ gtk_container_add(GTK_CONTAINER(w), hbox);
+ gtk_tool_item_set_expand(GTK_TOOL_ITEM(w), TRUE);
+ break;
+ case THROBBER_ITEM:
+ if (edit_mode)
+ return GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET(
+ gtk_image_new_from_pixbuf(
+ nsgtk_throbber->framedata[0])),
+ "[throbber]"));
+ image = GTK_WIDGET(gtk_image_new_from_pixbuf(
+ nsgtk_throbber->framedata[0]));
+ w = GTK_WIDGET(gtk_tool_item_new());
+ gtk_container_add(GTK_CONTAINER(w), image);
+ break;
+ case WEBSEARCH_ITEM:
+ if (edit_mode)
+ return GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET(
+ gtk_image_new_from_stock("gtk-find",
+ GTK_ICON_SIZE_LARGE_TOOLBAR)),
+ "[websearch]"));
+ hbox = gtk_hbox_new(FALSE, 0);
+ image = GTK_WIDGET(gtk_image_new_from_stock("gtk-info",
+ GTK_ICON_SIZE_LARGE_TOOLBAR));
+ entry = GTK_WIDGET(gtk_entry_new());
+ gtk_widget_set_size_request(entry, 77, -1);
+ gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
+ gtk_box_pack_end(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
+ w = GTK_WIDGET(gtk_tool_item_new());
+ gtk_container_add(GTK_CONTAINER(w), hbox);
+ break;
+#define MAKE_MENUBUTTON(p, q) case p##_BUTTON:\
+ label = remove_underscores(messages_get(#q), false);\
+ w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET(\
+ theme->image[p##_BUTTON]), label));\
+ free(label);\
+ break
+ MAKE_MENUBUTTON(NEWWINDOW, gtkNewWindow);
+ MAKE_MENUBUTTON(NEWTAB, gtkNewTab);
+ MAKE_MENUBUTTON(OPENFILE, gtkOpenFile);
+ MAKE_MENUBUTTON(CLOSETAB, gtkCloseTab);
+ MAKE_MENUBUTTON(CLOSEWINDOW, gtkCloseWindow);
+ MAKE_MENUBUTTON(SAVEPAGE, gtkSavePage);
+ MAKE_MENUBUTTON(PRINTPREVIEW, gtkPrintPreview);
+ MAKE_MENUBUTTON(PRINT, gtkPrint);
+ MAKE_MENUBUTTON(QUIT, gtkQuit);
+ MAKE_MENUBUTTON(CUT, gtkCut);
+ MAKE_MENUBUTTON(COPY, gtkCopy);
+ MAKE_MENUBUTTON(PASTE, gtkPaste);
+ MAKE_MENUBUTTON(DELETE, gtkDelete);
+ MAKE_MENUBUTTON(SELECTALL, gtkSelectAll);
+ MAKE_MENUBUTTON(PREFERENCES, gtkPreferences);
+ MAKE_MENUBUTTON(ZOOMPLUS, gtkZoomPlus);
+ MAKE_MENUBUTTON(ZOOMMINUS, gtkZoomMinus);
+ MAKE_MENUBUTTON(ZOOMNORMAL, gtkZoomNormal);
+ MAKE_MENUBUTTON(FULLSCREEN, gtkFullScreen);
+ MAKE_MENUBUTTON(VIEWSOURCE, gtkViewSource);
+ MAKE_MENUBUTTON(CONTENTS, gtkContents);
+ MAKE_MENUBUTTON(ABOUT, gtkAbout);
+ MAKE_MENUBUTTON(PDF, gtkPDF);
+ MAKE_MENUBUTTON(PLAINTEXT, gtkPlainText);
+ MAKE_MENUBUTTON(DRAWFILE, gtkDrawFile);
+ MAKE_MENUBUTTON(POSTSCRIPT, gtkPostScript);
+ MAKE_MENUBUTTON(FIND, gtkFind);
+ MAKE_MENUBUTTON(DOWNLOADS, gtkDownloads);
+ MAKE_MENUBUTTON(SAVEWINDOWSIZE, gtkSaveWindowSize);
+ MAKE_MENUBUTTON(TOGGLEDEBUGGING, gtkToggleDebugging);
+ MAKE_MENUBUTTON(SAVEBOXTREE, gtkSaveBoxTree);
+ MAKE_MENUBUTTON(SAVEDOMTREE, gtkSaveDomTree);
+ MAKE_MENUBUTTON(LOCALHISTORY, gtkLocalHistory);
+ MAKE_MENUBUTTON(GLOBALHISTORY, gtkGlobalHistory);
+ MAKE_MENUBUTTON(ADDBOOKMARKS, gtkAddBookMarks);
+ MAKE_MENUBUTTON(SHOWBOOKMARKS, gtkShowBookMarks);
+ MAKE_MENUBUTTON(OPENLOCATION, gtkOpenLocation);
+ MAKE_MENUBUTTON(NEXTTAB, gtkNextTab);
+ MAKE_MENUBUTTON(PREVTAB, gtkPrevTab);
+ MAKE_MENUBUTTON(GUIDE, gtkGuide);
+ MAKE_MENUBUTTON(INFO, gtkUserInformation);
+ default:
+ break;
+#undef MAKE_MENUBUTTON
+ }
+ return w;
+}
+
+/**
+ * \return toolbar item id when a widget is an element of the scaffolding
+ * else -1
+ */
+int nsgtk_toolbar_get_id_from_widget(GtkWidget *widget, struct gtk_scaffolding
+ *g)
+{
+ int i;
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ if ((nsgtk_scaffolding_button(g, i)->location != -1)
+ && (widget == GTK_WIDGET(
+ nsgtk_scaffolding_button(g, i)->button))) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/**
+ * \return toolbar item id from location when there is an item at that logical
+ * location; else -1
+ */
+int nsgtk_toolbar_get_id_at_location(struct gtk_scaffolding *g, int i)
+{
+ int q;
+ for (q = BACK_BUTTON; q < PLACEHOLDER_BUTTON; q++)
+ if (nsgtk_scaffolding_button(g, q)->location == i)
+ return q;
+ return -1;
+}
+
+void nsgtk_toolbar_connect_all(struct gtk_scaffolding *g)
+{
+ int q, i;
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ q = nsgtk_toolbar_get_id_at_location(g, i);
+ if (q == -1)
+ continue;
+ g_signal_connect(nsgtk_scaffolding_button(g, q)->button,
+ "size-allocate", G_CALLBACK(
+ nsgtk_scaffolding_toolbar_size_allocate), g);
+ nsgtk_toolbar_set_handler(g, q);
+ }
+}
+
+/**
+ * add handlers to factory widgets
+ * \param g the scaffolding to attach handlers to
+ * \param i the toolbar item id
+ */
+void nsgtk_toolbar_set_handler(struct gtk_scaffolding *g, int i)
+{
+ switch(i){
+ case URL_BAR_ITEM:
+ nsgtk_scaffolding_update_url_bar_ref(g);
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_urlbar(g)),
+ "activate", G_CALLBACK(
+ nsgtk_window_url_activate_event), g);
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_urlbar(g)),
+ "changed", G_CALLBACK(
+ nsgtk_window_url_changed), g);
+ break;
+ case THROBBER_ITEM:
+ nsgtk_scaffolding_update_throbber_ref(g);
+ break;
+ case WEBSEARCH_ITEM:
+ nsgtk_scaffolding_update_websearch_ref(g);
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_websearch(g)),
+ "activate", G_CALLBACK(
+ nsgtk_websearch_activate), g);
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_websearch(g)),
+ "focus-in-event", G_CALLBACK(
+ nsgtk_websearch_clear), g);
+ break;
+ default:
+ if (nsgtk_scaffolding_button(g, i)->bhandler != NULL)
+ g_signal_connect(nsgtk_scaffolding_button(g, i)->
+ button, "clicked",
+ G_CALLBACK(nsgtk_scaffolding_button(g,
+ i)->bhandler), g);
+ break;
+ }
+}
+
+#define DATAHANDLER(p, q)\
+gboolean nsgtk_toolbar_##p##_button_data(GtkWidget *widget, GdkDragContext\
+ *cont, GtkSelectionData *selection, guint info, guint time,\
+ gpointer data)\
+{\
+ window->currentbutton = q##_BUTTON;\
+ window->fromstore = true;\
+ return TRUE;\
+}\
+gboolean nsgtk_toolbar_##p##_toolbar_button_data(GtkWidget *widget,\
+ GdkDragContext *cont, GtkSelectionData *selection, guint info,\
+ guint time, gpointer data)\
+{\
+ window->currentbutton = q##_BUTTON;\
+ window->fromstore = false;\
+ return TRUE;\
+}
+
+DATAHANDLER(home, HOME)
+DATAHANDLER(forward, FORWARD)
+DATAHANDLER(back, BACK)
+DATAHANDLER(stop, STOP)
+DATAHANDLER(reload, RELOAD)
+DATAHANDLER(history, HISTORY)
+DATAHANDLER(newwindow, NEWWINDOW);
+DATAHANDLER(newtab, NEWTAB);
+DATAHANDLER(openfile, OPENFILE);
+DATAHANDLER(closetab, CLOSETAB);
+DATAHANDLER(closewindow, CLOSEWINDOW);
+DATAHANDLER(savepage, SAVEPAGE);
+DATAHANDLER(printpreview, PRINTPREVIEW);
+DATAHANDLER(print, PRINT);
+DATAHANDLER(quit, QUIT);
+DATAHANDLER(cut, CUT);
+DATAHANDLER(copy, COPY);
+DATAHANDLER(paste, PASTE);
+DATAHANDLER(delete, DELETE);
+DATAHANDLER(selectall, SELECTALL);
+DATAHANDLER(preferences, PREFERENCES);
+DATAHANDLER(zoomplus, ZOOMPLUS);
+DATAHANDLER(zoomminus, ZOOMMINUS);
+DATAHANDLER(zoomnormal, ZOOMNORMAL);
+DATAHANDLER(fullscreen, FULLSCREEN);
+DATAHANDLER(viewsource, VIEWSOURCE);
+DATAHANDLER(contents, CONTENTS);
+DATAHANDLER(about, ABOUT);
+DATAHANDLER(pdf, PDF);
+DATAHANDLER(plaintext, PLAINTEXT);
+DATAHANDLER(drawfile, DRAWFILE);
+DATAHANDLER(postscript, POSTSCRIPT);
+DATAHANDLER(find, FIND);
+DATAHANDLER(downloads, DOWNLOADS);
+DATAHANDLER(savewindowsize, SAVEWINDOWSIZE);
+DATAHANDLER(toggledebugging, TOGGLEDEBUGGING);
+DATAHANDLER(saveboxtree, SAVEBOXTREE);
+DATAHANDLER(savedomtree, SAVEDOMTREE);
+DATAHANDLER(localhistory, LOCALHISTORY);
+DATAHANDLER(globalhistory, GLOBALHISTORY);
+DATAHANDLER(addbookmarks, ADDBOOKMARKS);
+DATAHANDLER(showbookmarks, SHOWBOOKMARKS);
+DATAHANDLER(openlocation, OPENLOCATION);
+DATAHANDLER(nexttab, NEXTTAB);
+DATAHANDLER(prevtab, PREVTAB);
+DATAHANDLER(guide, GUIDE);
+DATAHANDLER(info, INFO);
+#undef DATAHANDLER
+#define DATAHANDLER(p, q)\
+gboolean nsgtk_toolbar_##p##_button_data(GtkWidget *widget, GdkDragContext\
+ *cont, GtkSelectionData *selection, guint info, guint time,\
+ gpointer data)\
+{\
+ window->currentbutton = q##_ITEM;\
+ window->fromstore = true;\
+ return TRUE;\
+}\
+gboolean nsgtk_toolbar_##p##_toolbar_button_data(GtkWidget *widget,\
+ GdkDragContext *cont, GtkSelectionData *selection, guint info,\
+ guint time, gpointer data)\
+{\
+ window->currentbutton = q##_ITEM;\
+ window->fromstore = false;\
+ return TRUE;\
+}
+DATAHANDLER(throbber, THROBBER);
+DATAHANDLER(websearch, WEBSEARCH);
+#undef DATAHANDLER
+
+/**
+ * connect temporary handler for toolbar edit events
+ */
+void nsgtk_toolbar_temp_connect(struct gtk_scaffolding *g, int i)
+{
+ if (i == URL_BAR_ITEM)
+ return;
+ g_signal_connect(nsgtk_scaffolding_button(g, i)->button,
+ "drag-data-get", G_CALLBACK(nsgtk_scaffolding_button(
+ g, i)->dataminus), g);
+}
+
+/**
+ * load toolbar settings from file
+ */
+void nsgtk_toolbar_customization_load(struct gtk_scaffolding *g)
+{
+ int i, ii;
+ char *val;
+ char buffer[SLEN("11;|") * 2 * PLACEHOLDER_BUTTON]; /* numbers 0-99 */
+ buffer[0] = '\0';
+ char *buffer1, *subbuffer, *ptr, *pter;
+ FILE *f = fopen(toolbar_indices_file_location, "r");
+ val = fgets(buffer, sizeof buffer, f);
+ if (val == NULL)
+ LOG(("empty read toolbar settings"));
+ fclose(f);
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++)
+ nsgtk_scaffolding_button(g, i)->location =
+ (i <= WEBSEARCH_ITEM) ? i : -1;
+ i = BACK_BUTTON;
+ ii = BACK_BUTTON;
+ buffer1 = strtok_r(buffer, "|", &ptr);
+ while (buffer1 != NULL) {
+ subbuffer = strtok_r(buffer1, ";", &pter);
+ i = atoi(subbuffer);
+ subbuffer = strtok_r(NULL, ";", &pter);
+ ii = atoi(subbuffer);
+ if ((i >= BACK_BUTTON) && (i < PLACEHOLDER_BUTTON) &&
+ (ii >= -1) && (ii < PLACEHOLDER_BUTTON)) {
+ nsgtk_scaffolding_button(g, i)->location = ii;
+ }
+ buffer1 = strtok_r(NULL, "|", &ptr);
+ }
+}
+
+/**
+ * cast toolbar settings to all scaffoldings referenced from the global linked
+ * list of gui_windows
+ */
+void nsgtk_toolbar_cast(struct gtk_scaffolding *g)
+{
+ int i;
+ nsgtk_scaffolding *list = scaf_list;
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++)
+ window->buttonlocations[i] = nsgtk_scaffolding_button(g, i)->
+ location;
+ while (list) {
+ if (list != g)
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++)
+ nsgtk_scaffolding_button(list, i)->location =
+ window->buttonlocations[i];
+ list = nsgtk_scaffolding_iterate(list);
+ }
+}
+
+/**
+ * save toolbar settings to file
+ */
+void nsgtk_toolbar_customization_save(struct gtk_scaffolding *g)
+{
+ int i;
+ FILE *f = fopen(toolbar_indices_file_location, "w");
+ if (f == NULL){
+ warn_user("gtkFileError", toolbar_indices_file_location);
+ return;
+ }
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ fprintf(f, "%d;%d|", i, nsgtk_scaffolding_button(g, i)->location);
+ }
+ fclose(f);
+}
+
Index: gtk/gtk_menu.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ gtk/gtk_menu.c 2009-07-10 12:49:36.000000000 +0100
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin(a)dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/ >.
+ */
+
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+#include <stdlib.h>
+#include "gtk/gtk_menu.h"
+#include "utils/messages.h"
+
+static struct nsgtk_export_submenu *nsgtk_menu_export_submenu(GtkAccelGroup *);
+static struct nsgtk_scaleview_submenu *nsgtk_menu_scaleview_submenu(
+ GtkAccelGroup *);
+static struct nsgtk_images_submenu *nsgtk_menu_images_submenu(GtkAccelGroup *);
+static struct nsgtk_toolbars_submenu *nsgtk_menu_toolbars_submenu(
+ GtkAccelGroup *);
+static struct nsgtk_debugging_submenu *nsgtk_menu_debugging_submenu(
+ GtkAccelGroup *);
+
+static unsigned int key;
+static GdkModifierType mod;
+
+#define IMAGE_ITEM(p, q, r)\
+ ret->q##_menuitem = GTK_IMAGE_MENU_ITEM(\
+ gtk_image_menu_item_new_with_mnemonic(\
+ messages_get(#r)));\
+ gtk_accelerator_parse(messages_get(#r "Accel"), &key, &mod);\
+ if (key > 0)\
+ gtk_widget_add_accelerator(GTK_WIDGET(ret->q##_menuitem),\
+ "activate", group, key, mod, GTK_ACCEL_VISIBLE);\
+ gtk_menu_shell_append(GTK_MENU_SHELL(ret->p##_menu),\
+ GTK_WIDGET(ret->q##_menuitem));\
+ gtk_widget_show(GTK_WIDGET(ret->q##_menuitem))
+#define CHECK_ITEM(p, q, r)\
+ ret->q##_menuitem = GTK_CHECK_MENU_ITEM(\
+ gtk_check_menu_item_new_with_mnemonic(\
+ messages_get(#r)));\
+ gtk_menu_shell_append(GTK_MENU_SHELL(ret->p##_menu),\
+ GTK_WIDGET(ret->q##_menuitem));\
+ gtk_widget_show(GTK_WIDGET(ret->q##_menuitem))
+
+#define SET_SUBMENU(q)\
+ ret->q##_submenu = nsgtk_menu_##q##_submenu(group);\
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(ret->q##_menuitem),\
+ GTK_WIDGET(ret->q##_submenu->q##_menu))
+#define ADD_SEP(q)\
+ w = gtk_separator_menu_item_new();\
+ gtk_menu_shell_append(GTK_MENU_SHELL(ret->q##_menu), w);\
+ gtk_widget_show(w)
+struct nsgtk_file_menu *nsgtk_menu_file_menu(GtkAccelGroup *group)
+{
+ GtkWidget *w;
+ struct nsgtk_file_menu *ret = malloc(sizeof(struct nsgtk_file_menu));
+ ret->file_menu = GTK_MENU(gtk_menu_new());
+ IMAGE_ITEM(file, newwindow, gtkNewWindow);
+ IMAGE_ITEM(file, newtab, gtkNewTab);
+ IMAGE_ITEM(file, openfile, gtkOpenFile);
+ IMAGE_ITEM(file, closewindow, gtkCloseWindow);
+ ADD_SEP(file);
+ IMAGE_ITEM(file, savepage, gtkSavePage);
+ IMAGE_ITEM(file, export, gtkExport);
+ ADD_SEP(file);
+ IMAGE_ITEM(file, printpreview, gtkPrintPreview);
+ IMAGE_ITEM(file, print, gtkPrint);
+ ADD_SEP(file);
+ IMAGE_ITEM(file, quit, gtkQuit);
+ SET_SUBMENU(export);
+ return ret;
+}
+
+struct nsgtk_edit_menu *nsgtk_menu_edit_menu(GtkAccelGroup *group)
+{
+ GtkWidget *w;
+ struct nsgtk_edit_menu *ret = malloc(sizeof(struct nsgtk_edit_menu));
+ ret->edit_menu = GTK_MENU(gtk_menu_new());
+ IMAGE_ITEM(edit, cut, gtkCut);
+ IMAGE_ITEM(edit, copy, gtkCopy);
+ IMAGE_ITEM(edit, paste, gtkPaste);
+ IMAGE_ITEM(edit, delete, gtkDelete);
+ ADD_SEP(edit);
+ IMAGE_ITEM(edit, selectall, gtkSelectAll);
+ ADD_SEP(edit);
+ IMAGE_ITEM(edit, find, gtkFind);
+ ADD_SEP(edit);
+ IMAGE_ITEM(edit, preferences, gtkPreferences);
+ return ret;
+}
+
+struct nsgtk_view_menu *nsgtk_menu_view_menu(GtkAccelGroup *group)
+{
+ GtkWidget *w;
+ struct nsgtk_view_menu *ret = malloc(sizeof(struct nsgtk_view_menu));
+ ret->view_menu = GTK_MENU(gtk_menu_new());
+ IMAGE_ITEM(view, stop, gtkStop);
+ IMAGE_ITEM(view, reload, gtkReload);
+ ADD_SEP(view);
+ IMAGE_ITEM(view, scaleview, gtkScaleView);
+ IMAGE_ITEM(view, fullscreen, gtkFullScreen);
+ IMAGE_ITEM(view, viewsource, gtkViewSource);
+ ADD_SEP(view);
+ IMAGE_ITEM(view, images, gtkImages);
+ IMAGE_ITEM(view, toolbars, gtkToolbars);
+ ADD_SEP(view);
+ IMAGE_ITEM(view, downloads, gtkDownloads);
+ IMAGE_ITEM(view, savewindowsize, gtkSaveWindowSize);
+ IMAGE_ITEM(view, debugging, gtkDebugging);
+ SET_SUBMENU(scaleview);
+ SET_SUBMENU(images);
+ SET_SUBMENU(toolbars);
+ SET_SUBMENU(debugging);
+ return ret;
+}
+
+struct nsgtk_nav_menu *nsgtk_menu_nav_menu(GtkAccelGroup *group)
+{
+ GtkWidget *w;
+ struct nsgtk_nav_menu *ret = malloc(sizeof(struct nsgtk_nav_menu));
+ ret->nav_menu = GTK_MENU(gtk_menu_new());
+ IMAGE_ITEM(nav, back, gtkBack);
+ IMAGE_ITEM(nav, forward, gtkForward);
+ IMAGE_ITEM(nav, home, gtkHome);
+ ADD_SEP(nav);
+ IMAGE_ITEM(nav, localhistory, gtkLocalHistory);
+ IMAGE_ITEM(nav, globalhistory, gtkGlobalHistory);
+ ADD_SEP(nav);
+ IMAGE_ITEM(nav, addbookmarks, gtkAddBookMarks);
+ IMAGE_ITEM(nav, showbookmarks, gtkShowBookMarks);
+ ADD_SEP(nav);
+ IMAGE_ITEM(nav, openlocation, gtkOpenLocation);
+ return ret;
+}
+
+struct nsgtk_tabs_menu *nsgtk_menu_tabs_menu(GtkAccelGroup *group)
+{
+ struct nsgtk_tabs_menu *ret = malloc(sizeof(struct nsgtk_tabs_menu));
+ ret->tabs_menu = GTK_MENU(gtk_menu_new());
+ IMAGE_ITEM(tabs, nexttab, gtkNextTab);
+ IMAGE_ITEM(tabs, prevtab, gtkPrevTab);
+ IMAGE_ITEM(tabs, closetab, gtkCloseTab);
+ return ret;
+}
+
+struct nsgtk_help_menu *nsgtk_menu_help_menu(GtkAccelGroup *group)
+{
+ GtkWidget *w;
+ struct nsgtk_help_menu *ret = malloc(sizeof(struct nsgtk_help_menu));
+ ret->help_menu = GTK_MENU(gtk_menu_new());
+ IMAGE_ITEM(help, contents, gtkContents);
+ IMAGE_ITEM(help, guide, gtkGuide);
+ IMAGE_ITEM(help, info, gtkUserInformation);
+ ADD_SEP(help);
+ IMAGE_ITEM(help, about, gtkAbout);
+ return ret;
+}
+
+struct nsgtk_export_submenu *nsgtk_menu_export_submenu(GtkAccelGroup *group)
+{
+ struct nsgtk_export_submenu *ret = malloc(sizeof(struct
+ nsgtk_export_submenu));
+ ret->export_menu = GTK_MENU(gtk_menu_new());
+ IMAGE_ITEM(export, plaintext, gtkPlainText);
+ IMAGE_ITEM(export, drawfile, gtkDrawFile);
+ IMAGE_ITEM(export, postscript, gtkPostScript);
+ IMAGE_ITEM(export, pdf, gtkPDF);
+ return ret;
+}
+
+struct nsgtk_scaleview_submenu *nsgtk_menu_scaleview_submenu(GtkAccelGroup
+ *group)
+{
+ struct nsgtk_scaleview_submenu *ret = malloc(sizeof(struct
+ nsgtk_scaleview_submenu));
+ ret->scaleview_menu = GTK_MENU(gtk_menu_new());
+ IMAGE_ITEM(scaleview, zoomplus, gtkZoomPlus);
+ IMAGE_ITEM(scaleview, zoomnormal, gtkZoomNormal);
+ IMAGE_ITEM(scaleview, zoomminus, gtkZoomMinus);
+ return ret;
+}
+struct nsgtk_images_submenu *nsgtk_menu_images_submenu(GtkAccelGroup *group)
+{
+ struct nsgtk_images_submenu *ret = malloc(sizeof(struct
+ nsgtk_images_submenu));
+ ret->images_menu = GTK_MENU(gtk_menu_new());
+ CHECK_ITEM(images, foregroundimages, gtkForegroundImages);
+ CHECK_ITEM(images, backgroundimages, gtkBackgroundImages);
+ return ret;
+}
+struct nsgtk_toolbars_submenu *nsgtk_menu_toolbars_submenu(GtkAccelGroup *group)
+{
+ struct nsgtk_toolbars_submenu *ret = malloc(sizeof(struct
+ nsgtk_toolbars_submenu));
+ ret->toolbars_menu = GTK_MENU(gtk_menu_new());
+ CHECK_ITEM(toolbars, menubar, gtkMenuBar);
+ gtk_check_menu_item_set_active(ret->menubar_menuitem, TRUE);
+ CHECK_ITEM(toolbars, toolbar, gtkToolBar);
+ gtk_check_menu_item_set_active(ret->toolbar_menuitem, TRUE);
+ CHECK_ITEM(toolbars, statusbar, gtkStatusBar);
+ gtk_check_menu_item_set_active(ret->statusbar_menuitem, TRUE);
+ return ret;
+}
+struct nsgtk_debugging_submenu *nsgtk_menu_debugging_submenu(GtkAccelGroup
+ *group)
+{
+ struct nsgtk_debugging_submenu *ret = malloc(sizeof(struct
+ nsgtk_debugging_submenu));
+ ret->debugging_menu = GTK_MENU(gtk_menu_new());
+ IMAGE_ITEM(debugging, toggledebugging, gtkToggleDebugging);
+ IMAGE_ITEM(debugging, saveboxtree, gtkSaveBoxTree);
+ IMAGE_ITEM(debugging, savedomtree, gtkSaveDomTree);
+ return ret;
+}
+
+#undef CHECK_ITEM
+#undef IMAGE_ITEM
+#undef SET_SUBMENU
+#undef ADD_SEP
+
Index: gtk/gtk_search.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ gtk/gtk_search.c 2009-07-10 12:49:36.000000000 +0100
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin(a)dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/ >.
+ */
+
+
+ /** \file
+ * Free text search (implementation)
+ */
+#include <ctype.h>
+#include <string.h>
+#include <gdk/gdkkeysyms.h>
+#include "gtk/gtk_search.h"
+#include "gtk/gtk_scaffolding.h"
+#include "gtk/gtk_window.h"
+#include "utils/config.h"
+#include "content/content.h"
+#include "desktop/browser.h"
+#include "desktop/gui.h"
+#include "desktop/search.h"
+#include "desktop/searchweb.h"
+#include "desktop/selection.h"
+#include "render/box.h"
+#include "render/html.h"
+#include "utils/config.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+
+void nsgtk_search_init(struct gtk_scaffolding *g);
+
+gboolean nsgtk_search_forward_button_clicked(GtkWidget *widget, gpointer data)
+{
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
+ nsgtk_search_init(g);
+ start_search(true);
+ return TRUE;
+}
+
+gboolean nsgtk_search_back_button_clicked(GtkWidget *widget, gpointer data)
+{
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
+ nsgtk_search_init(g);
+ start_search(false);
+ return TRUE;
+}
+
+void nsgtk_search_init(struct gtk_scaffolding *g)
+{
+ struct content *c;
+
+ assert(gui_window_get_browser_window(nsgtk_scaffolding_top_level(g))
+ != NULL);
+
+ c = gui_window_get_browser_window(nsgtk_scaffolding_top_level(g))->
+ current_content;
+
+ if ((!c) || (c->type != CONTENT_HTML && c->type != CONTENT_TEXTPLAIN))
+ return;
+
+ search_current_window = gui_window_get_browser_window(
+ nsgtk_scaffolding_top_level(g));
+ search_insert = true;
+}
+
+gboolean nsgtk_search_close_button_clicked(GtkWidget *widget, gpointer data)
+{
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
+ nsgtk_scaffolding_toggle_search_bar_visibility(g);
+ return TRUE;
+}
+
+gboolean nsgtk_search_entry_changed(GtkWidget *widget, gpointer data)
+{
+ gui_search_set_forward_state(false);
+ gui_search_set_back_state(false);
+ return TRUE;
+}
+
+gboolean nsgtk_search_entry_activate(GtkWidget *widget, gpointer data)
+{
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
+ nsgtk_search_init(g);
+ start_search(true);
+ return FALSE;
+}
+
+gboolean nsgtk_search_entry_key(GtkWidget *widget, GdkEventKey *event,
+ gpointer data)
+{
+ if (event->keyval == GDK_Escape) {
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
+ nsgtk_scaffolding_toggle_search_bar_visibility(g);
+ }
+ return FALSE;
+}
+
+gboolean nsgtk_websearch_activate(GtkWidget *widget, gpointer data)
+{
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
+ temp_open_background = 0;
+ search_web_new_window(gui_window_get_browser_window(
+ nsgtk_scaffolding_top_level(g)),
+ (char *)gtk_entry_get_text(nsgtk_scaffolding_search(
+ g)->entry));
+ temp_open_background = -1;
+ return TRUE;
+}
+
+gboolean nsgtk_websearch_clear(GtkWidget *widget, GdkEventFocus *f,
+ gpointer data)
+{
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
+ gtk_entry_set_text(nsgtk_scaffolding_search(g)->entry, "");
+ return FALSE;
+}
+
+void gui_search_set_status(bool found)
+{
+}
+
+void gui_search_set_hourglass(bool active)
+{
+}
+
+char *gui_search_get_string(void)
+{
+ struct gtk_scaffolding *g = nsgtk_get_scaffold(search_current_window->
+ window);
+ return (char *)gtk_entry_get_text(nsgtk_scaffolding_search(g)->entry);
+}
+
+void gui_search_add_recent(const char *string)
+{
+ recent_search[0] = strdup(string);
+}
+
+void gui_search_set_forward_state(bool inactive)
+{
+ if (search_current_window) {
+ struct gtk_scaffolding *g = nsgtk_get_scaffold(
+ search_current_window->window);
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_search(
+ g)->buttons[1]), inactive ? FALSE : TRUE);
+ }
+}
+
+void gui_search_set_back_state(bool inactive)
+{
+ if (search_current_window) {
+ struct gtk_scaffolding *g = nsgtk_get_scaffold(
+ search_current_window->window);
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_search(
+ g)->buttons[0]), inactive ? FALSE : TRUE);
+ }
+}
+
+bool gui_search_get_case_sens(void)
+{
+ struct gtk_scaffolding *g = nsgtk_get_scaffold(search_current_window->
+ window);
+ return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
+ nsgtk_scaffolding_search(g)->caseSens)) ? true : false;
+}
+
+bool gui_search_get_show_all(void)
+{
+ struct gtk_scaffolding *g = nsgtk_get_scaffold(search_current_window->
+ window);
+ return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
+ nsgtk_scaffolding_search(g)->checkAll)) ? true : false;
+}
Index: gtk/gtk_theme.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ gtk/gtk_theme.h 2009-07-10 12:49:37.000000000 +0100
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin(a)dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/ >.
+ */
+
+#ifndef _NETSURF_GTK_THEME_H_
+#define _NETSURF_GTK_THEME_H_
+
+#include <gtk/gtk.h>
+#include "gtk/gtk_scaffolding.h"
+
+extern char *current_theme_name;
+
+struct nsgtk_theme {
+ GtkImage *image[PLACEHOLDER_BUTTON];
+ GtkImage *searchimage[3]; /* back, forward, close */
+ /* apng throbber element */
+};
+
+struct nsgtk_theme *nsgtk_theme_load(GtkIconSize s);
+void nsgtk_theme_add(const char *themename);
+void nsgtk_theme_init(void);
+void nsgtk_theme_prepare(void);
+void nsgtk_theme_implement(struct gtk_scaffolding *g);
+
+#endif
Index: gtk/gtk_toolbar.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ gtk/gtk_toolbar.h 2009-07-10 12:49:37.000000000 +0100
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin(a)dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/ >.
+ */
+
+#ifndef _NETSURF_GTK_TOOLBAR_H_
+#define _NETSURF_GTK_TOOLBAR_H_
+
+#include <gtk/gtk.h>
+#include "gtk/gtk_scaffolding.h"
+
+void nsgtk_toolbar_customization_init(nsgtk_scaffolding *g);
+void nsgtk_toolbar_init(nsgtk_scaffolding *g);
+void nsgtk_toolbar_customization_load(nsgtk_scaffolding *g);
+void nsgtk_toolbar_set_physical(nsgtk_scaffolding *g);
+void nsgtk_toolbar_connect_all(nsgtk_scaffolding *g);
+int nsgtk_toolbar_get_id_from_widget(GtkWidget *widget, nsgtk_scaffolding
+ *g);
+
+#define TOOLPROTO(q) gboolean nsgtk_toolbar_##q##_button_data(\
+ GtkWidget *widget, GdkDragContext *cont, GtkSelectionData\
+ *selection, guint info, guint time, gpointer data);\
+gboolean nsgtk_toolbar_##q##_toolbar_button_data(GtkWidget *widget,\
+ GdkDragContext *cont, GtkSelectionData *selection, guint info,\
+ guint time, gpointer data)
+TOOLPROTO(home);
+TOOLPROTO(back);
+TOOLPROTO(forward);
+TOOLPROTO(reload);
+TOOLPROTO(stop);
+TOOLPROTO(throbber);
+TOOLPROTO(websearch);
+TOOLPROTO(history);
+TOOLPROTO(newwindow);
+TOOLPROTO(newtab);
+TOOLPROTO(openfile);
+TOOLPROTO(closetab);
+TOOLPROTO(closewindow);
+TOOLPROTO(savepage);
+TOOLPROTO(pdf);
+TOOLPROTO(plaintext);
+TOOLPROTO(drawfile);
+TOOLPROTO(postscript);
+TOOLPROTO(printpreview);
+TOOLPROTO(print);
+TOOLPROTO(quit);
+TOOLPROTO(cut);
+TOOLPROTO(copy);
+TOOLPROTO(paste);
+TOOLPROTO(delete);
+TOOLPROTO(selectall);
+TOOLPROTO(find);
+TOOLPROTO(preferences);
+TOOLPROTO(zoomplus);
+TOOLPROTO(zoomminus);
+TOOLPROTO(zoomnormal);
+TOOLPROTO(fullscreen);
+TOOLPROTO(viewsource);
+TOOLPROTO(downloads);
+TOOLPROTO(localhistory);
+TOOLPROTO(globalhistory);
+TOOLPROTO(addbookmarks);
+TOOLPROTO(showbookmarks);
+TOOLPROTO(openlocation);
+TOOLPROTO(nexttab);
+TOOLPROTO(prevtab);
+TOOLPROTO(savewindowsize);
+TOOLPROTO(toggledebugging);
+TOOLPROTO(saveboxtree);
+TOOLPROTO(savedomtree);
+TOOLPROTO(contents);
+TOOLPROTO(guide);
+TOOLPROTO(info);
+TOOLPROTO(about);
+#undef TOOLPROTO
+
+#endif
Index: gtk/gtk_menu.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ gtk/gtk_menu.h 2009-07-10 12:49:38.000000000 +0100
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin(a)dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/ >.
+ */
+#ifndef _NETSURF_GTK_MENU_H_
+#define _NETSURF_GTK_MENU_H_
+
+#include <gtk/gtk.h>
+
+struct nsgtk_file_menu {
+ GtkMenu *file_menu;
+ GtkImageMenuItem *newwindow_menuitem;
+ GtkImageMenuItem *newtab_menuitem;
+ GtkImageMenuItem *openfile_menuitem;
+ GtkImageMenuItem *closewindow_menuitem;
+ GtkImageMenuItem *savepage_menuitem;
+ GtkImageMenuItem *export_menuitem;
+ struct nsgtk_export_submenu *export_submenu;
+ GtkImageMenuItem *printpreview_menuitem;
+ GtkImageMenuItem *print_menuitem;
+ GtkImageMenuItem *quit_menuitem;
+};
+
+struct nsgtk_edit_menu {
+ GtkMenu *edit_menu;
+ GtkImageMenuItem *cut_menuitem;
+ GtkImageMenuItem *copy_menuitem;
+ GtkImageMenuItem *paste_menuitem;
+ GtkImageMenuItem *delete_menuitem;
+ GtkImageMenuItem *selectall_menuitem;
+ GtkImageMenuItem *find_menuitem;
+ GtkImageMenuItem *preferences_menuitem;
+};
+
+struct nsgtk_view_menu {
+ GtkMenu *view_menu;
+ GtkImageMenuItem *stop_menuitem;
+ GtkImageMenuItem *reload_menuitem;
+ GtkImageMenuItem *scaleview_menuitem;
+ struct nsgtk_scaleview_submenu *scaleview_submenu;
+ GtkImageMenuItem *fullscreen_menuitem;
+ GtkImageMenuItem *viewsource_menuitem;
+ GtkImageMenuItem *images_menuitem;
+ struct nsgtk_images_submenu *images_submenu;
+ GtkImageMenuItem *toolbars_menuitem;
+ struct nsgtk_toolbars_submenu *toolbars_submenu;
+ GtkImageMenuItem *downloads_menuitem;
+ GtkImageMenuItem *savewindowsize_menuitem;
+ GtkImageMenuItem *debugging_menuitem;
+ struct nsgtk_debugging_submenu *debugging_submenu;
+};
+
+struct nsgtk_nav_menu {
+ GtkMenu *nav_menu;
+ GtkImageMenuItem *back_menuitem;
+ GtkImageMenuItem *forward_menuitem;
+ GtkImageMenuItem *home_menuitem;
+ GtkImageMenuItem *localhistory_menuitem;
+ GtkImageMenuItem *globalhistory_menuitem;
+ GtkImageMenuItem *addbookmarks_menuitem;
+ GtkImageMenuItem *showbookmarks_menuitem;
+ GtkImageMenuItem *openlocation_menuitem;
+};
+
+struct nsgtk_tabs_menu {
+ GtkMenu *tabs_menu;
+ GtkImageMenuItem *nexttab_menuitem;
+ GtkImageMenuItem *prevtab_menuitem;
+ GtkImageMenuItem *closetab_menuitem;
+};
+
+struct nsgtk_help_menu {
+ GtkMenu *help_menu;
+ GtkImageMenuItem *contents_menuitem;
+ GtkImageMenuItem *guide_menuitem;
+ GtkImageMenuItem *info_menuitem;
+ GtkImageMenuItem *about_menuitem;
+};
+
+struct nsgtk_export_submenu {
+ GtkMenu *export_menu;
+ GtkImageMenuItem *plaintext_menuitem;
+ GtkImageMenuItem *drawfile_menuitem;
+ GtkImageMenuItem *postscript_menuitem;
+ GtkImageMenuItem *pdf_menuitem;
+};
+
+struct nsgtk_scaleview_submenu {
+ GtkMenu *scaleview_menu;
+ GtkImageMenuItem *zoomplus_menuitem;
+ GtkImageMenuItem *zoomminus_menuitem;
+ GtkImageMenuItem *zoomnormal_menuitem;
+};
+
+struct nsgtk_images_submenu {
+ GtkMenu *images_menu;
+ GtkCheckMenuItem *foregroundimages_menuitem;
+ GtkCheckMenuItem *backgroundimages_menuitem;
+};
+
+struct nsgtk_toolbars_submenu {
+ GtkMenu *toolbars_menu;
+ GtkCheckMenuItem *menubar_menuitem;
+ GtkCheckMenuItem *toolbar_menuitem;
+ GtkCheckMenuItem *statusbar_menuitem;
+};
+
+struct nsgtk_debugging_submenu {
+ GtkMenu *debugging_menu;
+ GtkImageMenuItem *toggledebugging_menuitem;
+ GtkImageMenuItem *saveboxtree_menuitem;
+ GtkImageMenuItem *savedomtree_menuitem;
+};
+
+struct nsgtk_file_menu *nsgtk_menu_file_menu(GtkAccelGroup *group);
+struct nsgtk_edit_menu *nsgtk_menu_edit_menu(GtkAccelGroup *group);
+struct nsgtk_view_menu *nsgtk_menu_view_menu(GtkAccelGroup *group);
+struct nsgtk_nav_menu *nsgtk_menu_nav_menu(GtkAccelGroup *group);
+struct nsgtk_tabs_menu *nsgtk_menu_tabs_menu(GtkAccelGroup *group);
+struct nsgtk_help_menu *nsgtk_menu_help_menu(GtkAccelGroup *group);
+
+#endif
Index: gtk/gtk_search.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ gtk/gtk_search.h 2009-07-10 12:49:38.000000000 +0100
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin(a)dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/ >.
+ */
+
+#ifndef _NETSURF_GTK_SEARCH_H_
+#define _NETSURF_GTK_SEARCH_H_
+
+#include <gtk/gtk.h>
+#include "gtk/gtk_scaffolding.h"
+
+void nsgtk_search_bar_toggle_visibility(struct gtk_scaffolding * g);
+gboolean nsgtk_search_entry_changed(GtkWidget *widget, gpointer data);
+gboolean nsgtk_search_entry_activate(GtkWidget *widget, gpointer data);
+gboolean nsgtk_search_entry_key(GtkWidget *widget, GdkEventKey *event,
+ gpointer data);
+gboolean nsgtk_search_forward_button_clicked(GtkWidget *widget, gpointer data);
+gboolean nsgtk_search_back_button_clicked(GtkWidget *widget, gpointer data);
+gboolean nsgtk_search_close_button_clicked(GtkWidget *widget, gpointer data);
+gboolean nsgtk_websearch_activate(GtkWidget *widget, gpointer data);
+gboolean nsgtk_websearch_clear(GtkWidget *widget, GdkEventFocus *f,
+ gpointer data);
+
+#endif
Index: gtk/res/warning.glade
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ gtk/res/warning.glade 2009-07-10 12:49:47.000000000 +0100
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--*- mode: xml -*-->
+<glade-interface>
+ <widget class="GtkWindow" id="wndWarning">
+ <property name="title" translatable="yes">Warning from NetSurf</property>
+ <property name="window_position">GTK_WIN_POS_CENTER</property>
+ <property name="icon_name">gtk-dialog-warning</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="urgency_hint">True</property>
+ <child>
+ <widget class="GtkVBox" id="vbox32">
+ <property name="visible">True</property>
+ <property name="border_width">2</property>
+ <child>
+ <widget class="GtkHBox" id="hbox30">
+ <property name="visible">True</property>
+ <property name="border_width">3</property>
+ <child>
+ <widget class="GtkImage" id="image519">
+ <property name="visible">True</property>
+ <property name="xpad">12</property>
+ <property name="icon_size">6</property>
+ <property name="icon_name">gtk-dialog-warning</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="labelWarning">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Help help help! I'm being held prisoner by a bunch of RISC OS zealots!</property>
+ <property name="wrap">True</property>
+ </widget>
+ <packing>
+ <property name="padding">1</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkHSeparator" id="hseparator2">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">3</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox2">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <widget class="GtkButton" id="button14">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">0</property>
+ <signal name="clicked" handler="gtk_widget_hide" object="wndWarning"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+</glade-interface>
Index: gtk/res/login.glade
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ gtk/res/login.glade 2009-07-10 12:49:47.000000000 +0100
@@ -0,0 +1,222 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--*- mode: xml -*-->
+<glade-interface>
+ <widget class="GtkDialog" id="wndLogin">
+ <property name="title" translatable="yes">Site Authentication</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ALWAYS</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox2">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox12">
+ <property name="visible">True</property>
+ <property name="border_width">3</property>
+ <child>
+ <widget class="GtkImage" id="image3">
+ <property name="visible">True</property>
+ <property name="yalign">0.10000000149011612</property>
+ <property name="xpad">12</property>
+ <property name="icon_size">6</property>
+ <property name="icon_name">gtk-dialog-authentication</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkTable" id="table5">
+ <property name="visible">True</property>
+ <property name="border_width">1</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">11</property>
+ <property name="row_spacing">10</property>
+ <child>
+ <widget class="GtkLabel" id="labelLoginHost">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">moo.yoo.com </property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label57">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Password</property>
+ </widget>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label56">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Username</property>
+ </widget>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label54">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Host</property>
+ </widget>
+ <packing>
+ <property name="x_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label55">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Realm</property>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="labelLoginRealm">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">my sekr3t area</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEntry" id="entryLoginPass">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="visibility">False</property>
+ <property name="activates_default">True</property>
+ <property name="text" translatable="yes">opensesame</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEntry" id="entryLoginUser">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="text" translatable="yes">sesame</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">1</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area2">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <widget class="GtkButton" id="buttonLoginCan">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="buttonLoginOK">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="response_id">-5</property>
+ <child>
+ <widget class="GtkAlignment" id="alignment14">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <child>
+ <widget class="GtkHBox" id="hbox11">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <widget class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="stock">gtk-ok</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label49">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Login</property>
+ <property name="use_underline">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+</glade-interface>
Index: gtk/res/themes
===================================================================
Index: gtk/res/themes/gtk+
===================================================================
Index: gtk/res/themes/gtk+/print.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/print.png differ
Index: gtk/res/themes/gtk+/closetab.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/closetab.png differ
Index: gtk/res/themes/gtk+/zoomnormal.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/zoomnormal.png differ
Index: gtk/res/themes/gtk+/closewindow.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/closewindow.png differ
Index: gtk/res/themes/gtk+/printpreview.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/printpreview.png differ
Index: gtk/res/themes/gtk+/zoomminus.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/zoomminus.png differ
Index: gtk/res/themes/gtk+/back.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/back.png differ
Index: gtk/res/themes/gtk+/preferences.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/preferences.png differ
Index: gtk/res/themes/gtk+/history.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/history.png differ
Index: gtk/res/themes/gtk+/openfile.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/openfile.png differ
Index: gtk/res/themes/gtk+/delete.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/delete.png differ
Index: gtk/res/themes/gtk+/fullscreen.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/fullscreen.png differ
Index: gtk/res/themes/gtk+/forward.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/forward.png differ
Index: gtk/res/themes/gtk+/reload.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/reload.png differ
Index: gtk/res/themes/gtk+/helpcontents.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/helpcontents.png differ
Index: gtk/res/themes/gtk+/info
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ gtk/res/themes/gtk+/info 2009-07-10 12:49:47.000000000 +0100
@@ -0,0 +1,81 @@
+This file is part of NetSurf, http://www.netsurf-browser.org/
+
+The images in this theme folder 'gtk+' are from the gtk stock image set
+http://library.gnome.org/devel/gtk/unstable/gtk-Stock-Items.html
+
+the image history.png is [for what it's worth!] Copyright 2009 Mark Benjamin
+<netsurf-browser.org.MarkBenjamin(a)dfgh.net>
+
+NetSurf is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; version 2 of the License.
+
+NetSurf is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/ >.
+
+*** Instructions for theming ***
+
+to create a theme, make a folder, whose name is the name of the theme;
+put in the folder, a set of png images for the toolbuttons;
+the names of the images should be a subset of the list
+
+ back.png,
+ history.png,
+ forward.png,
+ stop.png,
+ reload.png,
+ home.png,
+ newwindow.png,
+ newtab.png,
+ openfile.png,
+ closetab.png,
+ closewindow.png,
+ savepage.png,
+ pdf.png,
+ plaintext.png,
+ drawfile.png,
+ postscript.png,
+ printpreview.png,
+ print.png,
+ quit.png,
+ cut.png,
+ copy.png,
+ paste.png,
+ delete.png,
+ selectall.png,
+ find.png,
+ preferences.png,
+ zoomplus.png,
+ zoomminus.png,
+ zoomnormal.png,
+ fullscreen.png,
+ viewsource.png,
+ downloads.png,
+ savewindowsize.png,
+ toggledebugging.png,
+ saveboxtree.png,
+ savedomtree.png,
+ localhistory.png,
+ globalhistory.png,
+ addbookmarks.png,
+ showbookmarks.png,
+ openlocation.png,
+ nexttab.png,
+ prevtab.png,
+ contents.png,
+ guide.png,
+ info.png,
+ about.png,
+ searchback.png,
+ searchforward.png,
+ searchclose.png
+
+for local theming, the folder may be placed directly [as a subfolder] into the netsurf/gtk/res/themes folder; then 'add theme' from the preferences->advanced tab;
+
+for downloadable themes, compile netsurf/utils/container.c according to the instructions in the header of that file; make a netsurf container of the folder, serve it as content-type "application/x-netsurf-theme"; browse to it in NetSurf, then NetSurf should automatically install it
+
Index: gtk/res/themes/gtk+/selectall.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/selectall.png differ
Index: gtk/res/themes/gtk+/copy.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/copy.png differ
Index: gtk/res/themes/gtk+/paste.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/paste.png differ
Index: gtk/res/themes/gtk+/newtab.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/newtab.png differ
Index: gtk/res/themes/gtk+/newwindow.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/newwindow.png differ
Index: gtk/res/themes/gtk+/quit.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/quit.png differ
Index: gtk/res/themes/gtk+/helpabout.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/helpabout.png differ
Index: gtk/res/themes/gtk+/stop.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/stop.png differ
Index: gtk/res/themes/gtk+/home.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/home.png differ
Index: gtk/res/themes/gtk+/zoomplus.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/zoomplus.png differ
Index: gtk/res/themes/gtk+/cut.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/cut.png differ
Index: gtk/res/themes/gtk+/savepage.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/savepage.png differ
Index: gtk/res/themes/gtk+/viewsource.png
===================================================================
Binary files /dev/null and gtk/res/themes/gtk+/viewsource.png differ
Index: gtk/res/themes/Alpha.png
===================================================================
Binary files /dev/null and gtk/res/themes/Alpha.png differ
Index: gtk/res/default.ico
===================================================================
Binary files /dev/null and gtk/res/default.ico differ
Index: gtk/res/themelist
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ gtk/res/themelist 2009-07-10 12:49:47.000000000 +0100
@@ -0,0 +1,2 @@
+gtk default theme
+gtk+
Index: gtk/res/toolbarIndices
===================================================================
Index: gtk/res/ssl.glade
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ gtk/res/ssl.glade 2009-07-10 12:49:52.000000000 +0100
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--*- mode: xml -*-->
+<glade-interface>
+ <widget class="GtkDialog" id="wndSSLProblem">
+ <property name="border_width">1</property>
+ <property name="title" translatable="yes">SSL certificate problem</property>
+ <property name="modal">True</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox3">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox15">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkImage" id="image6">
+ <property name="visible">True</property>
+ <property name="yalign">0</property>
+ <property name="icon_size">6</property>
+ <property name="icon_name">gtk-dialog-warning</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vbox13">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkLabel" id="label62">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">NetSurf failed to verify the authenticity of an SSL certificate. Please verify the details presented below.</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkFrame" id="frame13">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <property name="label_xalign">0</property>
+ <child>
+ <widget class="GtkAlignment" id="alignment17">
+ <property name="visible">True</property>
+ <property name="left_padding">12</property>
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <child>
+ <widget class="GtkTextView" id="textview1">
+ <property name="height_request">200</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">False</property>
+ <property name="text" translatable="yes">(not implemented)</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label63">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Certificate chain</b></property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area3">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <widget class="GtkButton" id="sslreject">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="response_id">-6</property>
+ <child>
+ <widget class="GtkAlignment" id="alignment16">
+ <property name="visible">True</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <child>
+ <widget class="GtkHBox" id="hbox14">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <widget class="GtkImage" id="image5">
+ <property name="visible">True</property>
+ <property name="stock">gtk-cancel</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label61">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Reject</property>
+ <property name="use_underline">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="sslaccept">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="response_id">-5</property>
+ <child>
+ <widget class="GtkAlignment" id="alignment15">
+ <property name="visible">True</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <child>
+ <widget class="GtkHBox" id="hbox13">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <widget class="GtkImage" id="image4">
+ <property name="visible">True</property>
+ <property name="stock">gtk-apply</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label60">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Accept</property>
+ <property name="use_underline">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+</glade-interface>
Index: gtk/res/password.glade
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ gtk/res/password.glade 2009-07-10 12:49:52.000000000 +0100
@@ -0,0 +1,274 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--*- mode: xml -*-->
+<glade-interface>
+ <widget class="GtkWindow" id="wndPDFPassword">
+ <property name="title" translatable="yes">PDF Password</property>
+ <property name="modal">True</property>
+ <property name="window_position">GTK_WIN_POS_CENTER</property>
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="yalign">0.10000000149011612</property>
+ <property name="xpad">12</property>
+ <property name="icon_size">6</property>
+ <property name="icon_name">gtk-dialog-authentication</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <child>
+ <widget class="GtkLabel" id="labelInfo">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Write and confirm passwords:</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <child>
+ <widget class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Owner password:</property>
+ <property name="width_chars">15</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEntry" id="entryPDFOwnerPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">20</property>
+ <property name="visibility">False</property>
+ <property name="width_chars">20</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <child>
+ <widget class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Repeat password:</property>
+ <property name="width_chars">15</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEntry" id="entryPDFOwnerPassword1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">20</property>
+ <property name="visibility">False</property>
+ <property name="width_chars">20</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <child>
+ <widget class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">User password:</property>
+ <property name="width_chars">15</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEntry" id="entryPDFUserPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">20</property>
+ <property name="visibility">False</property>
+ <property name="width_chars">20</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox5">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <child>
+ <widget class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Repeat password:</property>
+ <property name="width_chars">15</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEntry" id="entryPDFUserPassword1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">20</property>
+ <property name="visibility">False</property>
+ <property name="width_chars">20</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox1">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <property name="spacing">10</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <widget class="GtkButton" id="buttonPDFSetPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="response_id">0</property>
+ <child>
+ <widget class="GtkHBox" id="hbox7">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkImage" id="image7">
+ <property name="visible">True</property>
+ <property name="stock">gtk-ok</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Set password</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="buttonPDFNoPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="response_id">0</property>
+ <child>
+ <widget class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox6">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkImage" id="image8">
+ <property name="visible">True</property>
+ <property name="stock">gtk-cancel</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">No password</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+</glade-interface>
Index: gtk/res/toolbar.glade
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ gtk/res/toolbar.glade 2009-07-10 12:49:52.000000000 +0100
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--Generated with glade3 3.4.5 on Tue Jun 9 08:21:40 2009 -->
+<glade-interface>
+ <widget class="GtkWindow" id="toolbarwindow">
+ <property name="width_request">700</property>
+ <property name="height_request">450</property>
+ <child>
+ <widget class="GtkVBox" id="windowvbox">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkLabel" id="toolbarlabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">move items from store to toolbar rearrange items in toolbar move items from toolbar to store</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <child>
+ <widget class="GtkViewport" id="viewport1">
+ <property name="visible">True</property>
+ <property name="resize_mode">GTK_RESIZE_QUEUE</property>
+ <child>
+ <widget class="GtkVBox" id="widgetvbox">
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="buttonhbox">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <widget class="GtkButton" id="resetbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="response_id">0</property>
+ <child>
+ <widget class="GtkHBox" id="button1hbox">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-refresh</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="refreshbuttonlabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">reset default</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">10</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <widget class="GtkButton" id="okbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="label" translatable="yes">gtk-apply</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">0</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">10</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="cancelbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="label" translatable="yes">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">0</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+</glade-interface>
Index: gtk/res/SearchEngines
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ gtk/res/SearchEngines 2009-07-10 12:49:52.000000000 +0100
@@ -0,0 +1,20 @@
+Google|www.google.com|http://www.google.com/search?q=%s|http://www.google.com/favicon.ico|
+Yahoo|search.yahoo.com|http://search.yahoo.com/search?p=%s|http://www.yahoo.com/favicon.ico|
+Bing|www.bing.com|http://www.bing.com/search?q=%s|http://www.bing.com/favicon.ico|
+Business.com|www.business.com|http://www.business.com/search/rslt_default.asp?query=%s|http://www.business.com/favicon.ico|
+Omgili|www.omgili.com|http://www.omgili.com/AAAAA/%s.html|http://www.omgili.com/favicon.ico|
+BBC News|search.bbc.co.uk|http://search.bbc.co.uk/search?q=%s&tab=ns|http://news.bbc.co.uk/favicon.ico|
+Ubuntu Packages|packages.ubuntu.com|http://packages.ubuntu.com/search?keywords=%...
+Creative Commons|creativecommons.org|http://creativecommons.org/?s=%s|http://creat...
+Ask.com|www.ask.com|http://www.ask.com/web?q=%s|http://www.ask.com/favicon.ico|
+Answers.com|www.answers.com|http://www.answers.com/%s|http://www.answers.com/favicon.ico|
+Dictionary.com|dictionary.reference.com|http://dictionary.reference.com/browse/%s?jss=0|http://dictionary.reference.com/favicon.ico|
+Youtube|www.youtube.com|http://www.youtube.com/results?search_query=%s|http://www.youtube.com/favicon.ico|
+AeroMp3|www.aeromp3.com|http://www.aeromp3.com/search?q=%s|http://www.aeromp3.com/favicon.ico|
+AOL|search.aol.com|http://search.aol.com/aol/search?query=%s|http://www.aol.com/favicon.ico|
+Baidu|www.baidu.com|http://www.baidu.com/s?wd=%s|http://www.baidu.com/favicon.ico|
+Amazon|www.amazon.com|http://www.amazon.com/s/ref=nb_ss_gw?field-keywords=%s|http://www.amazon.com/favicon.ico|
+Ebay|shop.ebay.com|http://shop.ebay.com/items/%s|http://www.ebay.com/favicon.ico|
+IMDB|www.imdb.com|http://www.imdb.com/find?q=%s|http://www.imdb.com/favicon.ico|
+ESPN|search.espn.go.com|http://search.espn.go.com/%s/|http://www.espn.go.com/favicon.ico|
+Wikipedia|en.wikipedia.org|http://en.wikipedia.org/w/index.php?title=Special%%3ASearch&search=%s|http://en.wikipedia.org/favicon.ico|
Index: !NetSurf/Resources/default.ico
===================================================================
Binary files /dev/null and !NetSurf/Resources/default.ico differ
Index: !NetSurf/Resources/SearchEngines
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ !NetSurf/Resources/SearchEngines 2009-07-10 12:49:58.000000000 +0100
@@ -0,0 +1,20 @@
+Google|www.google.com|http://www.google.com/search?q=%s|http://www.google.com/favicon.ico|
+Yahoo|search.yahoo.com|http://search.yahoo.com/search?p=%s|http://www.yahoo.com/favicon.ico|
+Bing|www.bing.com|http://www.bing.com/search?q=%s|http://www.bing.com/favicon.ico|
+Business.com|www.business.com|http://www.business.com/search/rslt_default.asp?query=%s|http://www.business.com/favicon.ico|
+Omgili|www.omgili.com|http://www.omgili.com/AAAAA/%s.html|http://www.omgili.com/favicon.ico|
+BBC News|search.bbc.co.uk|http://search.bbc.co.uk/search?q=%s&tab=ns|http://news.bbc.co.uk/favicon.ico|
+Ubuntu Packages|packages.ubuntu.com|http://packages.ubuntu.com/search?keywords=%...
+Creative Commons|creativecommons.org|http://creativecommons.org/?s=%s|http://creat...
+Ask.com|www.ask.com|http://www.ask.com/web?q=%s|http://www.ask.com/favicon.ico|
+Answers.com|www.answers.com|http://www.answers.com/%s|http://www.answers.com/favicon.ico|
+Dictionary.com|dictionary.reference.com|http://dictionary.reference.com/browse/%s?jss=0|http://dictionary.reference.com/favicon.ico|
+Youtube|www.youtube.com|http://www.youtube.com/results?search_query=%s|http://www.youtube.com/favicon.ico|
+AeroMp3|www.aeromp3.com|http://www.aeromp3.com/search?q=%s|http://www.aeromp3.com/favicon.ico|
+AOL|search.aol.com|http://search.aol.com/aol/search?query=%s|http://www.aol.com/favicon.ico|
+Baidu|www.baidu.com|http://www.baidu.com/s?wd=%s|http://www.baidu.com/favicon.ico|
+Amazon|www.amazon.com|http://www.amazon.com/s/ref=nb_ss_gw?field-keywords=%s|http://www.amazon.com/favicon.ico|
+Ebay|shop.ebay.com|http://shop.ebay.com/items/%s|http://www.ebay.com/favicon.ico|
+IMDB|www.imdb.com|http://www.imdb.com/find?q=%s|http://www.imdb.com/favicon.ico|
+ESPN|search.espn.go.com|http://search.espn.go.com/%s/|http://www.espn.go.com/favicon.ico|
+Wikipedia|en.wikipedia.org|http://en.wikipedia.org/w/index.php?title=Special%%3ASearch&search=%s|http://en.wikipedia.org/favicon.ico|
Index: Docs/BUILDING-AmigaCross
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ Docs/BUILDING-AmigaCross 2009-07-10 12:49:59.000000000 +0100
@@ -0,0 +1,11 @@
+to install an Amiga cross-compiler in a Linux distribution, there are instructions at
+
+http://utilitybase.com/article/show/2007/06/23/231/Installing+an+AmigaOS+4+cross+compiler
+
+a more Mac-oriented article is at
+http://utilitybase.com/article/show/2006/05/21/188/Building+Amiga+OS+4+GCC+Cross+Compiler+for+UNIX%252FMAC
+
+more background at
+http://cross.zerohero.se/os4.html
+
+then install linked libs in the correct place
Index: beos/beos_search.cpp
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ beos/beos_search.cpp 2009-07-10 12:50:00.000000000 +0100
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin(a)dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/ >.
+ */
+
+#include <stdbool.h>
+#include <string.h>
+
+extern "C" {
+#include "utils/log.h"
+}
+
+/* put new search_web globals here for now */
+char *search_engines_file_location;
+char *search_default_ico_location;
+
+/**
+ * Change the displayed search status.
+ * \param found search pattern matched in text
+ */
+void gui_search_set_status(bool found)
+{
+}
+
+/**
+ * display hourglass while searching
+ * \param active start/stop indicator
+ */
+void gui_search_set_hourglass(bool active)
+{
+}
+
+/**
+ * retrieve string being searched for from gui
+ */
+char *gui_search_get_string(void)
+{
+}
+
+/**
+ * add search string to recent searches list
+ * \param string search pattern
+ */
+void gui_search_add_recent(const char *string)
+{
+}
+
+/**
+ * activate search forwards button in gui
+ * \param active activate/inactivate
+ */
+void gui_search_set_forward_state(bool inactive)
+{
+}
+
+/**
+ * activate search forwards button in gui
+ * \param active activate/inactivate
+ */
+void gui_search_set_back_state(bool inactive)
+{
+}
+
+/**
+ * retrieve state of 'case sensitive' check in gui
+ */
+bool gui_search_get_case_sens(void)
+{
+}
+
+/**
+ * retrieve state of 'show all' check in gui
+ */
+bool gui_search_get_show_all(void)
+{
+}
Index: riscos/searchweb.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ riscos/searchweb.c 2009-07-10 12:50:10.000000000 +0100
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin(a)dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/ >.
+ */
+
+#include <string.h>
+#include "content/content.h"
+#include "desktop/searchweb.h"
+
+char *search_engines_file_location;
+char *search_default_ico_location;
+struct content *search_ico;
Index: desktop/searchweb.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ desktop/searchweb.c 2009-07-10 12:50:16.000000000 +0100
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin(a)dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/ >.
+ */
+
+ /** \file
+ * Free text search (core)
+ */
+#include "utils/config.h"
+
+#include <ctype.h>
+#include <string.h>
+#include "content/content.h"
+#include "content/fetchcache.h"
+#include "content/fetch.h"
+#include "desktop/browser.h"
+#include "desktop/gui.h"
+#include "desktop/options.h"
+#include "desktop/searchweb.h"
+#include "utils/config.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/url.h"
+#include "utils/utils.h"
+
+static struct search_provider {
+ char *name;
+ char *hostname;
+ char *searchstring;
+ char *ico;
+} current_search_provider;
+
+struct content *search_ico = 0;
+
+bool search_web_new_window(struct browser_window *bw, const char *searchterm)
+{
+ char *encsearchterm;
+ char *url;
+ if (url_escape(searchterm,0, true, NULL, &encsearchterm) !=
+ URL_FUNC_OK)
+ return false;
+ url = search_web_get_url(encsearchterm);
+ browser_window_create(url, bw, NULL,
+ false, true);
+ free(url);
+ return true;
+}
+
+bool search_is_url(const char *url)
+{
+ char *url2, *host;
+
+ if (url_normalize(url, &url2) != URL_FUNC_OK)
+ return false;
+
+ if (url_host(url2, &host) != URL_FUNC_OK)
+ return false;
+
+ return true;
+}
+/**
+ * caches the details of the current web search provider
+ * \param reference the enum value of the provider
+ * browser init code [as well as changing preferences code] should call
+ * search_web_provider_details(option_search_provider)
+ */
+
+void search_web_provider_details(int reference)
+{
+ char buf[300];
+ char delim[2];
+ int ref = 0;
+ if (search_engines_file_location == NULL)
+ return;
+ FILE *f = fopen(search_engines_file_location, "r");
+ if (f == NULL)
+ return;
+ while (fgets(buf, sizeof(buf), f)) {
+ if (buf[0] == '\0')
+ continue;
+ buf[strlen(buf)-1] = '\0';
+ if (ref++ == reference)
+ break;
+ }
+ strcpy(delim, "|");
+ if (current_search_provider.name)
+ free(current_search_provider.name);
+ current_search_provider.name = strdup(strtok(buf, delim));
+ if (current_search_provider.hostname)
+ free(current_search_provider.hostname);
+ current_search_provider.hostname = strdup(strtok(NULL, delim));
+ if (current_search_provider.searchstring)
+ free(current_search_provider.searchstring);
+ current_search_provider.searchstring = strdup(strtok(NULL, delim));
+ if (current_search_provider.ico)
+ free(current_search_provider.ico);
+ current_search_provider.ico = strdup(strtok(NULL, delim));
+ return;
+}
+
+char *search_web_from_term(const char *searchterm)
+{
+ char *encsearchterm;
+ if (url_escape(searchterm, 0, true, NULL, &encsearchterm)
+ != URL_FUNC_OK)
+ return strdup(searchterm);
+ return search_web_get_url(encsearchterm);
+}
+
+char *search_web_provider_name()
+{
+ if (current_search_provider.name)
+ return strdup(current_search_provider.name);
+ return strdup("google");
+}
+
+char *search_web_provider_host()
+{
+ if (current_search_provider.hostname)
+ return strdup(current_search_provider.hostname);
+ return strdup("www.google.com ");
+}
+
+char *search_web_ico_name()
+{
+ if (current_search_provider.ico)
+ return strdup(current_search_provider.ico);
+ return strdup("http://www.google.com/favicon.ico ");
+}
+
+char *search_web_get_url(const char *encsearchterm)
+{
+ char *pref, *ret;
+ int len;
+ if (current_search_provider.searchstring)
+ pref = strdup(current_search_provider.searchstring);
+ else
+ pref = strdup("http://www.google.com/search?q=%s ");
+ len = strlen(encsearchterm) + strlen(pref);
+ ret = malloc(len -1);
+ snprintf(ret, len-1, pref, encsearchterm);
+ free(pref);
+ return ret;
+}
+
+struct content *search_web_retrieve_ico(bool localdefault)
+{
+ char *url;
+ if (localdefault) {
+ if (search_default_ico_location == NULL)
+ return NULL;
+ url = malloc(SLEN("file://") + strlen(
+ search_default_ico_location) + 1);
+ strcpy(url, "file://");
+ strcat(url, search_default_ico_location);
+ } else {
+ url = search_web_ico_name();
+ }
+
+ struct content *icocontent = NULL;
+ if (url != NULL)
+ icocontent = fetchcache(url, search_web_ico_callback,
+ 0, 0, 20, 20, true, 0,
+ 0, false, false);
+ free(url);
+ if (icocontent == NULL)
+ return NULL;
+
+ fetchcache_go(icocontent, 0, search_web_ico_callback,
+ 0, 0, 20, 20,
+ 0, 0, false, 0);
+
+ if (icocontent == NULL) {
+ LOG(("web search ico loading delayed"));
+ return NULL;
+ }
+ return icocontent;
+}
+
+void search_web_ico_callback(content_msg msg, struct content *ico,
+ intptr_t p1, intptr_t p2, union content_msg_data data)
+{
+
+ switch (msg) {
+ case CONTENT_MSG_LOADING:
+ case CONTENT_MSG_READY:
+ break;
+
+ case CONTENT_MSG_DONE:
+ LOG(("got favicon '%s'", ico->url));
+ if (ico->type == CONTENT_ICO) {
+ search_ico = ico; /* cache */
+ gui_window_set_search_ico();
+ } else {
+ search_web_retrieve_ico(true);
+ }
+ break;
+
+ case CONTENT_MSG_LAUNCH:
+ case CONTENT_MSG_ERROR:
+ LOG(("favicon %s error: %s", ico->url, data.error));
+ ico = 0;
+ search_web_retrieve_ico(true);
+ break;
+
+ case CONTENT_MSG_STATUS:
+ case CONTENT_MSG_NEWPTR:
+ case CONTENT_MSG_AUTH:
+ case CONTENT_MSG_SSL:
+ break;
+
+ default:
+ assert(0);
+ }
+}
Index: desktop/searchweb.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ desktop/searchweb.h 2009-07-10 12:50:17.000000000 +0100
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin(a)dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/ >.
+ */
+
+#ifndef _NETSURF_DESKTOP_SEARCH_WEB_H_
+#define _NETSURF_DESKTOP_SEARCH_WEB_H_
+
+#include <ctype.h>
+#include <string.h>
+#include "content/content.h"
+#include "desktop/browser.h"
+
+extern char *search_engines_file_location;
+extern char *search_default_ico_location;
+extern struct content *search_ico;
+
+typedef enum {
+ OSP_GOOGLE,
+ OSP_YAHOO,
+ OSP_MICROSOFT,
+ OSP_BUSINESS,
+ OSP_OMGILI,
+ OSP_BBC,
+ OSP_UBUNTU,
+ OSP_COMMONS,
+ OSP_ASK,
+ OSP_ANSWERS,
+ OSP_DICTIONARYCOM,
+ OSP_YOUTUBE,
+ OSP_AEROMP3,
+ OSP_AOL,
+ OSP_BAIDU,
+ OSP_AMAZON,
+ OSP_EBAY,
+ OSP_IMDB,
+ OSP_ESPN,
+ OSP_WIKIPEDIA
+} default_search_provider;
+
+/**
+ * open new tab/window for web search term
+ */
+bool search_web_new_window(struct browser_window *bw, const char *searchterm);
+
+/**
+ * retrieve full search url from unencoded search term
+ */
+char *search_web_from_term(const char *searchterm);
+
+/**
+ * retrieve full search url from encoded web search term
+ */
+char *search_web_get_url(const char *encsearchterm);
+
+/**
+ * cache details of web search provider from file
+ */
+void search_web_provider_details(int reference);
+
+/**
+ * retrieve name of web search provider
+ */
+char *search_web_provider_name(void);
+
+/**
+ * retrieve hostname of web search provider
+ */
+char *search_web_provider_host(void);
+
+/**
+ * retrieve name of .ico for search bar
+ */
+char *search_web_ico_name(void);
+
+/**
+ * check whether an URL is in fact a search term
+ * \param url the url being checked
+ * \return true for url, false for search
+ */
+bool search_is_url(const char *url);
+
+struct content *search_web_retrieve_ico(bool localdefault);
+
+void search_web_ico_callback(content_msg msg, struct content *ico,
+ intptr_t p1, intptr_t p2, union content_msg_data data);
+
+#endif
Index: desktop/search.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ desktop/search.c 2009-07-10 12:50:19.000000000 +0100
@@ -0,0 +1,630 @@
+/*
+ * Copyright 2004 John M Bell <jmb202(a)ecs.soton.ac.uk>
+ * Copyright 2005 Adrian Lees <adrianl(a)users.sourceforge.net>
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin(a)dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/ >.
+ */
+
+ /** \file
+ * Free text search (core)
+ */
+#include "utils/config.h"
+
+#include <ctype.h>
+#include <string.h>
+#include "content/content.h"
+#include "desktop/browser.h"
+#include "desktop/gui.h"
+#include "desktop/options.h"
+#include "desktop/search.h"
+#include "desktop/selection.h"
+#include "render/box.h"
+#include "render/html.h"
+#include "utils/config.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/url.h"
+#include "utils/utils.h"
+
+
+#ifndef NOF_ELEMENTS
+#define NOF_ELEMENTS(array) (sizeof(array)/sizeof(*(array)))
+#endif
+
+
+struct list_entry {
+ unsigned start_idx; /* start position of match */
+ unsigned end_idx; /* end of match */
+
+ struct box *start_box; /* used only for html contents */
+ struct box *end_box;
+
+ struct selection *sel;
+
+ struct list_entry *prev;
+ struct list_entry *next;
+};
+
+struct browser_window *search_current_window = NULL;
+static char *search_string = NULL;
+static struct list_entry search_head = { 0, 0, NULL, NULL, NULL, NULL, NULL };
+static struct list_entry *search_found = &search_head;
+static struct list_entry *search_current = NULL;
+static struct content *search_content = NULL;
+static bool search_prev_case_sens = false;
+
+bool search_insert;
+char *recent_search[RECENT_SEARCHES];
+
+static void do_search(const char *string, int string_len, bool case_sens,
+ bool forwards);
+static const char *find_pattern(const char *string, int s_len,
+ const char *pattern, int p_len, bool case_sens,
+ unsigned int *m_len);
+static bool find_occurrences_html(const char *pattern, int p_len,
+ struct box *cur, bool case_sens);
+static bool find_occurrences_text(const char *pattern, int p_len,
+ struct content *c, bool case_sens);
+static struct list_entry *add_entry(unsigned start_idx, unsigned end_idx);
+static void free_matches(void);
+
+/**
+ * Begins/continues the search process
+ * Note that this may be called many times for a single search.
+ *
+ * \param forwards search forwards from start/current position
+ */
+
+void start_search(bool forwards)
+{
+ int string_len;
+ const char *string;
+ int i = 0;
+
+ string = gui_search_get_string();
+ assert(string);
+
+ gui_search_add_recent(string);
+
+ string_len = strlen(string);
+ for(i = 0; i < string_len; i++)
+ if (string[i] != '#' && string[i] != '*') break;
+ if (i >= string_len) {
+ free_matches();
+ gui_search_set_status(true);
+ gui_search_set_back_state(true);
+ gui_search_set_forward_state(true);
+ gui_window_set_scroll(search_current_window->window, 0, 0);
+ return;
+ }
+
+ do_search(string, string_len, gui_search_get_case_sens(), forwards);
+}
+
+/**
+ * Release the memory used by the list of matches,
+ * deleting selection objects too
+ */
+
+void free_matches(void)
+{
+ struct list_entry *a = search_found->next;
+ struct list_entry *b;
+
+ /* empty the list before clearing and deleting the
+ selections because the the clearing updates the
+ screen immediately, causing nested accesses to the list */
+
+ search_found->prev = 0;
+ search_found->next = 0;
+
+ for (; a; a = b) {
+ b = a->next;
+ if (a->sel) {
+ selection_clear(a->sel, true);
+ selection_destroy(a->sel);
+ }
+ free(a);
+ }
+}
+
+/**
+ * Search for a string in the box tree
+ *
+ * \param string the string to search for
+ * \param string_len length of search string
+ * \param case_sens whether to perform a case sensitive search
+ * \param forwards direction to search in
+ */
+void do_search(const char *string, int string_len, bool case_sens,
+ bool forwards)
+{
+ struct rect bounds;
+ struct content *c;
+ struct box *box;
+ bool new = false;
+
+ if (!search_current_window)
+ return;
+ c = search_current_window->current_content;
+
+ /* only handle html contents */
+ if ((!c) || (c->type != CONTENT_HTML &&
+ c->type != CONTENT_TEXTPLAIN))
+ return;
+
+ box = c->data.html.layout;
+
+ if (!box)
+ return;
+
+ /* LOG(("do_search '%s' - '%s' (%p, %p) %p (%d, %d) %d",
+ search_string, string, search_content, c, search_found->next,
+ search_prev_case_sens, case_sens, forwards)); */
+
+ /* check if we need to start a new search or continue an old one */
+ if (!search_string || c != search_content || !search_found->next ||
+ search_prev_case_sens != case_sens ||
+ (case_sens && strcmp(string, search_string) != 0) ||
+ (!case_sens && strcasecmp(string, search_string) != 0)) {
+ bool res;
+
+ if (search_string)
+ free(search_string);
+ search_current = 0;
+ free_matches();
+
+ search_string = malloc(string_len + 1);
+ if (search_string) {
+ memcpy(search_string, string, string_len);
+ search_string[string_len] = '\0';
+ }
+
+ gui_search_set_hourglass(true);
+
+ if (c->type == CONTENT_HTML)
+ res = find_occurrences_html(string, string_len,
+ box, case_sens);
+ else {
+ assert(c->type == CONTENT_TEXTPLAIN);
+ res = find_occurrences_text(string, string_len,
+ c, case_sens);
+ }
+
+ if (!res) {
+ free_matches();
+ gui_search_set_hourglass(false);
+ return;
+ }
+ gui_search_set_hourglass(false);
+
+ new = true;
+ search_content = c;
+ search_prev_case_sens = case_sens;
+ }
+
+ /* LOG(("%d %p %p (%p, %p)", new, search_found->next, search_current,
+ search_current->prev, search_current->next)); */
+
+ if (new) {
+ /* new search, beginning at the top of the page */
+ search_current = search_found->next;
+ }
+ else if (search_current) {
+ /* continued search in the direction specified */
+ if (forwards) {
+ if (search_current->next)
+ search_current = search_current->next;
+ }
+ else {
+ if (search_current->prev)
+ search_current = search_current->prev;
+ }
+ }
+
+ gui_search_set_status(search_current != NULL);
+ search_show_all(gui_search_get_show_all());
+
+ gui_search_set_back_state(!search_current || !search_current->prev);
+ gui_search_set_forward_state(!search_current || !search_current->next);
+
+ if (!search_current)
+ return;
+
+ switch (c->type) {
+ case CONTENT_HTML:
+ /* get box position and jump to it */
+ box_coords(search_current->start_box,
+ &bounds.x0, &bounds.y0);
+ /* \todo: move x0 in by correct idx */
+ box_coords(search_current->end_box,
+ &bounds.x1, &bounds.y1);
+ /* \todo: move x1 in by correct idx */
+ bounds.x1 += search_current->end_box->width;
+ bounds.y1 += search_current->end_box->height;
+ break;
+
+ default:
+ assert(c->type == CONTENT_TEXTPLAIN);
+ textplain_coords_from_range(c,
+ search_current->start_idx,
+ search_current->end_idx, &bounds);
+ break;
+ }
+
+ gui_window_scroll_visible(search_current_window->window,
+ bounds.x0, bounds.y0, bounds.x1, bounds.y1);
+}
+
+/**
+ * Find the first occurrence of 'match' in 'string' and return its index
+ *
+ * /param string the string to be searched (unterminated)
+ * /param s_len length of the string to be searched
+ * /param pattern the pattern for which we are searching (unterminated)
+ * /param p_len length of pattern
+ * /param case_sens true iff case sensitive match required
+ * /param m_len accepts length of match in bytes
+ * /return pointer to first match, NULL if none
+ */
+
+const char *find_pattern(const char *string, int s_len, const char *pattern,
+ int p_len, bool case_sens, unsigned int *m_len)
+{
+ struct { const char *ss, *s, *p; bool first; } context[16];
+ const char *ep = pattern + p_len;
+ const char *es = string + s_len;
+ const char *p = pattern - 1; /* a virtual '*' before the pattern */
+ const char *ss = string;
+ const char *s = string;
+ bool first = true;
+ int top = 0;
+
+ while (p < ep) {
+ bool matches;
+ if (p < pattern || *p == '*') {
+ char ch;
+
+ /* skip any further asterisks; one is the same as many
+ */
+ do p++; while (p < ep && *p == '*');
+
+ /* if we're at the end of the pattern, yes, it matches
+ */
+ if (p >= ep) break;
+
+ /* anything matches a # so continue matching from
+ here, and stack a context that will try to match
+ the wildcard against the next character */
+
+ ch = *p;
+ if (ch != '#') {
+ /* scan forwards until we find a match for
+ this char */
+ if (!case_sens) ch = toupper(ch);
+ while (s < es) {
+ if (case_sens) {
+ if (*s == ch) break;
+ } else if (toupper(*s) == ch)
+ break;
+ s++;
+ }
+ }
+
+ if (s < es) {
+ /* remember where we are in case the match
+ fails; we may then resume */
+ if (top < (int)NOF_ELEMENTS(context)) {
+ context[top].ss = ss;
+ context[top].s = s + 1;
+ context[top].p = p - 1;
+ /* ptr to last asterisk */
+ context[top].first = first;
+ top++;
+ }
+
+ if (first) {
+ ss = s;
+ /* remember first non-'*' char */
+ first = false;
+ }
+
+ matches = true;
+ }
+ else
+ matches = false;
+ }
+ else if (s < es) {
+ char ch = *p;
+ if (ch == '#')
+ matches = true;
+ else {
+ if (case_sens)
+ matches = (*s == ch);
+ else
+ matches = (toupper(*s) == toupper(ch));
+ }
+ if (matches && first) {
+ ss = s; /* remember first non-'*' char */
+ first = false;
+ }
+ }
+ else
+ matches = false;
+
+ if (matches) {
+ p++; s++;
+ }
+ else {
+ /* doesn't match, resume with stacked context if we have one */
+ if (--top < 0) return NULL; /* no match, give up */
+
+ ss = context[top].ss;
+ s = context[top].s;
+ p = context[top].p;
+ first = context[top].first;
+ }
+ }
+
+ /* end of pattern reached */
+ *m_len = max(s - ss, 1);
+ return ss;
+}
+
+/**
+ * Finds all occurrences of a given string in the html box tree
+ *
+ * \param pattern the string pattern to search for
+ * \param p_len pattern length
+ * \param cur pointer to the current box
+ * \param case_sens whether to perform a case sensitive search
+ * \return true on success, false on memory allocation failure
+ */
+bool find_occurrences_html(const char *pattern, int p_len, struct box *cur,
+ bool case_sens)
+{
+ struct box *a;
+
+ /* ignore this box, if there's no visible text */
+ if (!cur->object && cur->text) {
+ const char *text = cur->text;
+ unsigned length = cur->length;
+
+ while (length > 0) {
+ struct list_entry *entry;
+ unsigned match_length;
+ unsigned match_offset;
+ const char *new_text;
+ const char *pos = find_pattern(text, length,
+ pattern, p_len, case_sens,
+ &match_length);
+ if (!pos) break;
+
+ /* found string in box => add to list */
+ match_offset = pos - cur->text;
+
+ entry = add_entry(cur->byte_offset + match_offset,
+ cur->byte_offset +
+ match_offset +
+ match_length);
+ if (!entry)
+ return false;
+
+ entry->start_box = cur;
+ entry->end_box = cur;
+
+ new_text = pos + match_length;
+ length -= (new_text - text);
+ text = new_text;
+ }
+ }
+
+ /* and recurse */
+ for (a = cur->children; a; a = a->next) {
+ if (!find_occurrences_html(pattern, p_len, a, case_sens))
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Finds all occurrences of a given string in a textplain content
+ *
+ * \param pattern the string pattern to search for
+ * \param p_len pattern length
+ * \param c the content to be searched
+ * \param case_sens wheteher to perform a case sensitive search
+ * \return true on success, false on memory allocation failure
+ */
+
+bool find_occurrences_text(const char *pattern, int p_len,
+ struct content *c, bool case_sens)
+{
+ int nlines = textplain_line_count(c);
+ int line;
+
+ for(line = 0; line < nlines; line++) {
+ size_t offset, length;
+ const char *text = textplain_get_line(c, line,
+ &offset, &length);
+ if (text) {
+ while (length > 0) {
+ struct list_entry *entry;
+ unsigned match_length;
+ size_t start_idx;
+ const char *new_text;
+ const char *pos = find_pattern(text, length,
+ pattern, p_len, case_sens,
+ &match_length);
+ if (!pos) break;
+
+ /* found string in line => add to list */
+ start_idx = offset + (pos - text);
+ entry = add_entry(start_idx, start_idx +
+ match_length);
+ if (!entry)
+ return false;
+
+ new_text = pos + match_length;
+ offset += (new_text - text);
+ length -= (new_text - text);
+ text = new_text;
+ }
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Determines whether any portion of the given text box should be
+ * selected because it matches the current search string.
+ *
+ * \param g gui window
+ * \param start_offset byte offset within text of string to be checked
+ * \param end_offset byte offset within text
+ * \param start_idx byte offset within string of highlight start
+ * \param end_idx byte offset of highlight end
+ * \return true iff part of the box should be highlighted
+ */
+
+bool gui_search_term_highlighted(struct gui_window *g,
+ unsigned start_offset, unsigned end_offset,
+ unsigned *start_idx, unsigned *end_idx)
+{
+ if (g == search_current_window->window) {
+ struct list_entry *a;
+ for(a = search_found->next; a; a = a->next)
+ if (a->sel && selection_defined(a->sel) &&
+ selection_highlighted(a->sel,
+ start_offset, end_offset,
+ start_idx, end_idx))
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Specifies whether all matches or just the current match should
+ * be highlighted in the search text.
+ */
+
+void search_show_all(bool all)
+{
+ struct list_entry *a;
+
+ for (a = search_found->next; a; a = a->next) {
+ bool add = true;
+ if (!all && a != search_current) {
+ add = false;
+ if (a->sel) {
+ selection_clear(a->sel, true);
+ selection_destroy(a->sel);
+ a->sel = NULL;
+ }
+ }
+ if (add && !a->sel) {
+ a->sel = selection_create(search_current_window);
+ if (a->sel) {
+ struct content *c = search_current_window->
+ current_content;
+ switch (c->type) {
+ case CONTENT_HTML:
+ selection_init(a->sel,
+ c->data.html.layout);
+ break;
+ default:
+ assert(c->type ==
+ CONTENT_TEXTPLAIN);
+ selection_init(a->sel, NULL);
+ break;
+ }
+ selection_set_start(a->sel, a->start_idx);
+ selection_set_end(a->sel, a->end_idx);
+ }
+ }
+ }
+}
+
+/**
+ * Add a new entry to the list of matches
+ *
+ * \param start_idx offset of match start within textual representation
+ * \param end_idx offset of match end
+ * \return pointer to added entry, NULL iff failed
+ */
+
+struct list_entry *add_entry(unsigned start_idx, unsigned end_idx)
+{
+ struct list_entry *entry;
+
+ /* found string in box => add to list */
+ entry = calloc(1, sizeof(*entry));
+ if (!entry) {
+ warn_user("NoMemory", 0);
+ return NULL;
+ }
+
+ entry->start_idx = start_idx;
+ entry->end_idx = end_idx;
+ entry->sel = NULL;
+
+ entry->next = 0;
+ entry->prev = search_found->prev;
+ if (!search_found->prev)
+ search_found->next = entry;
+ else
+ search_found->prev->next = entry;
+ search_found->prev = entry;
+
+ return entry;
+}
+
+/**
+ * clears the state of the search, including resetting buttons
+ * to their active state
+ */
+void search_reset()
+{
+ gui_search_set_forward_state(false);
+ gui_search_set_back_state(false);
+ search_end();
+}
+
+/**
+ * Ends the search process, invalidating all global state
+ * freeing the list of found boxes
+ */
+void search_end()
+{
+ search_current_window = 0;
+
+ if (search_string) {
+ gui_search_add_recent(search_string);
+ free(search_string);
+ }
+ search_string = 0;
+
+ free_matches();
+
+ search_current = 0;
+
+ search_content = 0;
+
+ search_prev_case_sens = false;
+}
Index: desktop/search.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ desktop/search.h 2009-07-10 12:50:19.000000000 +0100
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin(a)dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/ >.
+ */
+
+#ifndef _NETSURF_DESKTOP_SEARCH_H_
+#define _NETSURF_DESKTOP_SEARCH_H_
+
+#include <ctype.h>
+#include <string.h>
+
+#define RECENT_SEARCHES 8
+
+extern char *recent_search[RECENT_SEARCHES];
+extern bool search_insert;
+
+void start_search(bool forwards);
+
+/**
+ * Change the displayed search status.
+ * \param found search pattern matched in text
+ */
+void gui_search_set_status(bool found);
+
+/**
+ * display hourglass while searching
+ * \param active start/stop indicator
+ */
+void gui_search_set_hourglass(bool active);
+
+/**
+ * retrieve string being searched for from gui
+ */
+char *gui_search_get_string(void);
+
+/**
+ * add search string to recent searches list
+ * \param string search pattern
+ */
+void gui_search_add_recent(const char *string);
+
+/**
+ * activate search forwards button in gui
+ * \param active activate/inactivate
+ */
+void gui_search_set_forward_state(bool inactive);
+
+/**
+ * activate search forwards button in gui
+ * \param active activate/inactivate
+ */
+void gui_search_set_back_state(bool inactive);
+
+/**
+ * retrieve state of 'case sensitive' check in gui
+ */
+bool gui_search_get_case_sens(void);
+
+/**
+ * retrieve state of 'show all' check in gui
+ */
+bool gui_search_get_show_all(void);
+
+void search_show_all(bool all);
+
+void search_reset(void);
+void search_end(void);
+
+#endif
Index: desktop/save_complete.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ desktop/save_complete.c 2009-07-10 12:50:20.000000000 +0100
@@ -0,0 +1,742 @@
+/*
+ * Copyright 2004 John M Bell <jmb202(a)ecs.soton.ac.uk>
+ * Copyright 2004-2007 James Bursa <bursa(a)users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/ >.
+ */
+
+/** \file
+ * Save HTML document with dependencies (implementation).
+ */
+
+#include "utils/config.h"
+
+#define _GNU_SOURCE /* for strndup */
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <regex.h>
+#include <libxml/HTMLtree.h>
+#include <libxml/parserInternals.h>
+#include "utils/config.h"
+#include "content/content.h"
+#include "css/css.h"
+#include "render/box.h"
+#include "desktop/save_complete.h"
+#include "utils/log.h"
+#include "utils/url.h"
+#include "utils/utils.h"
+
+regex_t save_complete_import_re;
+
+/** An entry in save_complete_list. */
+struct save_complete_entry {
+ struct content *content;
+ struct save_complete_entry *next; /**< Next entry in list */
+};
+
+#ifdef RISCOS
+ static char pathsep = '.';
+#else
+ static char pathsep = '/';
+#endif
+
+/** List of urls seen and saved so far. */
+static struct save_complete_entry *save_complete_list = 0;
+
+static bool save_complete_html(struct content *c, const char *path,
+ bool index);
+static bool save_imported_sheets(struct content *c, const char *path);
+static char * rewrite_stylesheet_urls(const char *source, unsigned int size,
+ int *osize, const char *base);
+static bool rewrite_document_urls(xmlDoc *doc, const char *base);
+static bool rewrite_urls(xmlNode *n, const char *base);
+static bool rewrite_url(xmlNode *n, const char *attr, const char *base);
+static bool save_complete_list_add(struct content *content);
+static struct content * save_complete_list_find(const char *url);
+static bool save_complete_list_check(struct content *content);
+/* static void save_complete_list_dump(void); */
+static bool save_complete_inventory(const char *path);
+
+/**
+ * Save an HTML page with all dependencies.
+ *
+ * \param c CONTENT_HTML to save
+ * \param path directory to save to (must exist)
+ * \return true on success, false on error and error reported
+ */
+
+bool save_complete(struct content *c, const char *path)
+{
+ bool result;
+
+ result = save_complete_html(c, path, true);
+
+ if (result)
+ result = save_complete_inventory(path);
+
+ /* free save_complete_list */
+ while (save_complete_list) {
+ struct save_complete_entry *next = save_complete_list->next;
+ free(save_complete_list);
+ save_complete_list = next;
+ }
+
+ return result;
+}
+
+
+/**
+ * Save an HTML page with all dependencies, recursing through imported pages.
+ *
+ * \param c CONTENT_HTML to save
+ * \param path directory to save to (must exist)
+ * \param index true to save as "index"
+ * \return true on success, false on error and error reported
+ */
+
+bool save_complete_html(struct content *c, const char *path, bool index)
+{
+ char filename[256];
+ unsigned int i;
+ xmlDocPtr doc;
+ bool res;
+
+ if (c->type != CONTENT_HTML)
+ return false;
+
+ if (save_complete_list_check(c))
+ return true;
+
+ /* save stylesheets, ignoring the base and adblocking sheets */
+ for (i = STYLESHEET_STYLE; i != c->data.html.stylesheet_count; i++) {
+ struct content *css = c->data.html.stylesheet_content[i];
+ char *source;
+ int source_len;
+
+ if (!css)
+ continue;
+ if (save_complete_list_check(css))
+ continue;
+
+ if (i != STYLESHEET_STYLE) {
+ if (!save_complete_list_add(css)) {
+ warn_user("NoMemory", 0);
+ return false;
+ }
+ }
+
+ if (!save_imported_sheets(css, path))
+ return false;
+
+ if (i == STYLESHEET_STYLE)
+ continue; /* don't save <style> elements */
+
+ snprintf(filename, sizeof filename, "%x", (unsigned int) css);
+ source = rewrite_stylesheet_urls(css->source_data,
+ css->source_size, &source_len, css->url);
+ if (!source) {
+ warn_user("NoMemory", 0);
+ return false;
+ }
+ res = save_complete_gui_save(path, filename, NULL, source_len,
+ source, 0xf79);
+ free(source);
+ if (res == false)
+ return false;
+ }
+
+ /* save objects */
+ for (i = 0; i != c->data.html.object_count; i++) {
+ struct content *obj = c->data.html.object[i].content;
+
+ /* skip difficult content types */
+ if (!obj || obj->type >= CONTENT_OTHER || !obj->source_data)
+ continue;
+ if (save_complete_list_check(obj))
+ continue;
+
+ if (!save_complete_list_add(obj)) {
+ warn_user("NoMemory", 0);
+ return false;
+ }
+
+ if (obj->type == CONTENT_HTML) {
+ if (!save_complete_html(obj, path, false))
+ return false;
+ continue;
+ }
+
+ snprintf(filename, sizeof filename, "%x", (unsigned int) obj);
+ res = save_complete_gui_save(path, filename, obj,
+ obj->source_size, obj->source_data, 0);
+ if(res == false)
+ return false;
+ }
+
+ /*save_complete_list_dump();*/
+
+ /* copy document */
+ doc = xmlCopyDoc(c->data.html.document, 1);
+ if (doc == NULL) {
+ warn_user("NoMemory", 0);
+ return false;
+ }
+
+ /* rewrite all urls we know about */
+ if (!rewrite_document_urls(doc, c->data.html.base_url)) {
+ xmlFreeDoc(doc);
+ warn_user("NoMemory", 0);
+ return false;
+ }
+
+ /* save the html file out last of all */
+ if (index)
+ snprintf(filename, sizeof filename, "index");
+ else
+ snprintf(filename, sizeof filename, "%x", (unsigned int)c);
+
+ errno = 0;
+ if (save_complete_htmlSaveFileFormat(path, filename, doc, 0, 0) == -1) {
+ if (errno)
+ warn_user("SaveError", strerror(errno));
+ else
+ warn_user("SaveError", "htmlSaveFileFormat failed");
+
+ xmlFreeDoc(doc);
+ return false;
+ }
+
+ xmlFreeDoc(doc);
+
+ return save_complete_gui_filetype(path, filename, 0xfaf);
+}
+
+
+/**
+ * Save stylesheets imported by a CONTENT_CSS.
+ *
+ * \param c a CONTENT_CSS
+ * \param path path to save to
+ * \return true on success, false on error and error reported
+ */
+
+bool save_imported_sheets(struct content *c, const char *path)
+{
+ char filename[256];
+ unsigned int j;
+ char *source;
+ int source_len;
+ bool res;
+
+ for (j = 0; j != c->data.css.import_count; j++) {
+ struct content *css = c->data.css.import_content[j];
+
+ if (!css)
+ continue;
+ if (save_complete_list_check(css))
+ continue;
+
+ if (!save_complete_list_add(css)) {
+ warn_user("NoMemory", 0);
+ return false;
+ }
+
+ if (!save_imported_sheets(css, path))
+ return false;
+
+ snprintf(filename, sizeof filename, "%x", (unsigned int) css);
+ source = rewrite_stylesheet_urls(css->source_data,
+ css->source_size, &source_len, css->url);
+ if (!source) {
+ warn_user("NoMemory", 0);
+ return false;
+ }
+
+ res = save_complete_gui_save(path, filename, NULL, source_len,
+ source, 0xf79);
+ free(source);
+ if (res == false)
+ return false;
+ }
+
+ return true;
+}
+
+
+/**
+ * Initialise the save_complete module.
+ */
+
+void save_complete_init(void)
+{
+ /* Match an @import rule - see CSS 2.1 G.1. */
+ regcomp_wrapper(&save_complete_import_re,
+ "@import" /* IMPORT_SYM */
+ "[ \t\r\n\f]*" /* S* */
+ /* 1 */
+ "(" /* [ */
+ /* 2 3 */
+ "\"(([^\"]|[\\]\")*)\"" /* STRING (approximated) */
+ "|"
+ /* 4 5 */
+ "'(([^']|[\\]')*)'"
+ "|" /* | */
+ "url\\([ \t\r\n\f]*" /* URI (approximated) */
+ /* 6 7 */
+ "\"(([^\"]|[\\]\")*)\""
+ "[ \t\r\n\f]*\\)"
+ "|"
+ "url\\([ \t\r\n\f]*"
+ /* 8 9 */
+ "'(([^']|[\\]')*)'"
+ "[ \t\r\n\f]*\\)"
+ "|"
+ "url\\([ \t\r\n\f]*"
+ /* 10 */
+ "([^) \t\r\n\f]*)"
+ "[ \t\r\n\f]*\\)"
+ ")", /* ] */
+ REG_EXTENDED | REG_ICASE);
+}
+
+
+/**
+ * Rewrite stylesheet \@import rules for save complete.
+ *
+ * @param source stylesheet source
+ * @param size size of source
+ * @param osize updated with the size of the result
+ * @param base url of stylesheet
+ * @return converted source, or 0 on out of memory
+ */
+
+char * rewrite_stylesheet_urls(const char *source, unsigned int size,
+ int *osize, const char *base)
+{
+ char *res;
+ const char *url;
+ char *url2;
+ char buf[20];
+ unsigned int offset = 0;
+ int url_len = 0;
+ struct content *content;
+ int m;
+ unsigned int i;
+ unsigned int imports = 0;
+ regmatch_t match[11];
+ url_func_result result;
+
+ /* count number occurences of @import to (over)estimate result size */
+ /* can't use strstr because source is not 0-terminated string */
+ for (i = 0; 7 < size && i != size - 7; i++) {
+ if (source[i] == '@' &&
+ tolower(source[i + 1]) == 'i' &&
+ tolower(source[i + 2]) == 'm' &&
+ tolower(source[i + 3]) == 'p' &&
+ tolower(source[i + 4]) == 'o' &&
+ tolower(source[i + 5]) == 'r' &&
+ tolower(source[i + 6]) == 't')
+ imports++;
+ }
+
+ res = malloc(size + imports * 20);
+ if (!res)
+ return 0;
+ *osize = 0;
+
+ while (offset < size) {
+ m = regexec(&save_complete_import_re, source + offset,
+ 11, match, 0);
+ if (m)
+ break;
+
+ /*for (unsigned int i = 0; i != 11; i++) {
+ if (match[i].rm_so == -1)
+ continue;
+ fprintf(stderr, "%i: '%.*s'\n", i,
+ match[i].rm_eo - match[i].rm_so,
+ source + offset + match[i].rm_so);
+ }*/
+
+ url = 0;
+ if (match[2].rm_so != -1) {
+ url = source + offset + match[2].rm_so;
+ url_len = match[2].rm_eo - match[2].rm_so;
+ } else if (match[4].rm_so != -1) {
+ url = source + offset + match[4].rm_so;
+ url_len = match[4].rm_eo - match[4].rm_so;
+ } else if (match[6].rm_so != -1) {
+ url = source + offset + match[6].rm_so;
+ url_len = match[6].rm_eo - match[6].rm_so;
+ } else if (match[8].rm_so != -1) {
+ url = source + offset + match[8].rm_so;
+ url_len = match[8].rm_eo - match[8].rm_so;
+ } else if (match[10].rm_so != -1) {
+ url = source + offset + match[10].rm_so;
+ url_len = match[10].rm_eo - match[10].rm_so;
+ }
+ assert(url);
+
+ url2 = strndup(url, url_len);
+ if (!url2) {
+ free(res);
+ return 0;
+ }
+ result = url_join(url2, base, (char**)&url);
+ free(url2);
+ if (result == URL_FUNC_NOMEM) {
+ free(res);
+ return 0;
+ }
+
+ /* copy data before match */
+ memcpy(res + *osize, source + offset, match[0].rm_so);
+ *osize += match[0].rm_so;
+
+ if (result == URL_FUNC_OK) {
+ content = save_complete_list_find(url);
+ if (content) {
+ /* replace import */
+ snprintf(buf, sizeof buf, "@import '%x'",
+ (unsigned int) content);
+ memcpy(res + *osize, buf, strlen(buf));
+ *osize += strlen(buf);
+ } else {
+ /* copy import */
+ memcpy(res + *osize, source + offset + match[0].rm_so,
+ match[0].rm_eo - match[0].rm_so);
+ *osize += match[0].rm_eo - match[0].rm_so;
+ }
+ }
+ else {
+ /* copy import */
+ memcpy(res + *osize, source + offset + match[0].rm_so,
+ match[0].rm_eo - match[0].rm_so);
+ *osize += match[0].rm_eo - match[0].rm_so;
+ }
+
+ assert(0 < match[0].rm_eo);
+ offset += match[0].rm_eo;
+ }
+
+ /* copy rest of source */
+ if (offset < size) {
+ memcpy(res + *osize, source + offset, size - offset);
+ *osize += size - offset;
+ }
+
+ return res;
+}
+
+
+/**
+ * Rewrite URLs in a HTML document to be relative.
+ *
+ * \param doc root of the document tree
+ * \param base base url of document
+ * \return true on success, false on out of memory
+ */
+
+bool rewrite_document_urls(xmlDoc *doc, const char *base)
+{
+ xmlNode *node;
+
+ for (node = doc->children; node; node = node->next)
+ if (node->type == XML_ELEMENT_NODE)
+ if (!rewrite_urls(node, base))
+ return false;
+
+ return true;
+}
+
+
+/**
+ * Traverse tree, rewriting URLs as we go.
+ *
+ * \param n xmlNode of type XML_ELEMENT_NODE to rewrite
+ * \param base base url of document
+ * \return true on success, false on out of memory
+ *
+ * URLs in the tree rooted at element n are rewritten.
+ */
+
+bool rewrite_urls(xmlNode *n, const char *base)
+{
+ xmlNode *child;
+
+ assert(n->type == XML_ELEMENT_NODE);
+
+ /**
+ * We only need to consider the following cases:
+ *
+ * Attribute: Elements:
+ *
+ * 1) data <object>
+ * 2) href <a> <area> <link>
+ * 3) src <script> <input> <frame> <iframe> <img>
+ * 4) n/a <style>
+ * 5) n/a any <base> tag
+ * 6) background any (except those above)
+ */
+ if (!n->name) {
+ /* ignore */
+ }
+ /* 1 */
+ else if (strcmp((const char *) n->name, "object") == 0) {
+ if (!rewrite_url(n, "data", base))
+ return false;
+ }
+ /* 2 */
+ else if (strcmp((const char *) n->name, "a") == 0 ||
+ strcmp((const char *) n->name, "area") == 0 ||
+ strcmp((const char *) n->name, "link") == 0) {
+ if (!rewrite_url(n, "href", base))
+ return false;
+ }
+ /* 3 */
+ else if (strcmp((const char *) n->name, "frame") == 0 ||
+ strcmp((const char *) n->name, "iframe") == 0 ||
+ strcmp((const char *) n->name, "input") == 0 ||
+ strcmp((const char *) n->name, "img") == 0 ||
+ strcmp((const char *) n->name, "script") == 0) {
+ if (!rewrite_url(n, "src", base))
+ return false;
+ }
+ /* 4 */
+ else if (strcmp((const char *) n->name, "style") == 0) {
+ unsigned int len;
+ xmlChar *content;
+
+ for (child = n->children; child != 0; child = child->next) {
+ /* Get current content */
+ content = xmlNodeGetContent(child);
+ if (!content)
+ /* unfortunately we don't know if this is
+ * due to memory exhaustion, or because
+ * there is no content for this node */
+ continue;
+
+ /* Rewrite @import rules */
+ char *rewritten = rewrite_stylesheet_urls(
+ (const char *) content,
+ strlen((const char *) content),
+ (int *) &len, base);
+ xmlFree(content);
+ if (!rewritten)
+ return false;
+
+ /* set new content */
+ xmlNodeSetContentLen(child,
+ (const xmlChar*)rewritten,
+ len);
+ }
+
+ return true;
+ }
+ /* 5 */
+ else if (strcmp((const char *) n->name, "base") == 0) {
+ /* simply remove any <base> tags from the document */
+ xmlUnlinkNode(n);
+ xmlFreeNode(n);
+ /* base tags have no content, so there's no point recursing
+ * additionally, we've just destroyed this node, so trying
+ * to recurse would result in bad things happening */
+ return true;
+ }
+ /* 6 */
+ else {
+ if (!rewrite_url(n, "background", base))
+ return false;
+ }
+
+ /* now recurse */
+ for (child = n->children; child;) {
+ /* we must extract the next child now, as if the current
+ * child is a <base> element, it will be removed from the
+ * tree (see 5, above), thus preventing extraction of the
+ * next child */
+ xmlNode *next = child->next;
+ if (child->type == XML_ELEMENT_NODE) {
+ if (!rewrite_urls(child, base))
+ return false;
+ }
+ child = next;
+ }
+
+ return true;
+}
+
+
+/**
+ * Rewrite an URL in a HTML document.
+ *
+ * \param n The node to modify
+ * \param attr The html attribute to modify
+ * \param base base url of document
+ * \return true on success, false on out of memory
+ */
+
+bool rewrite_url(xmlNode *n, const char *attr, const char *base)
+{
+ char *url, *data;
+ char rel[20];
+ struct content *content;
+ url_func_result res;
+
+ if (!xmlHasProp(n, (const xmlChar *) attr))
+ return true;
+
+ data = (char *) xmlGetProp(n, (const xmlChar *) attr);
+ if (!data)
+ return false;
+
+ res = url_join(data, base, &url);
+ xmlFree(data);
+ if (res == URL_FUNC_NOMEM)
+ return false;
+ else if (res == URL_FUNC_OK) {
+ content = save_complete_list_find(url);
+ if (content) {
+ /* found a match */
+ free(url);
+ snprintf(rel, sizeof rel, "%x",
+ (unsigned int) content);
+ if (!xmlSetProp(n, (const xmlChar *) attr,
+ (xmlChar *) rel))
+ return false;
+ } else {
+ /* no match found */
+ if (!xmlSetProp(n, (const xmlChar *) attr,
+ (xmlChar *) url)) {
+ free(url);
+ return false;
+ }
+ free(url);
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Add a content to the save_complete_list.
+ *
+ * \param content content to add
+ * \return true on success, false on out of memory
+ */
+
+bool save_complete_list_add(struct content *content)
+{
+ struct save_complete_entry *entry;
+ entry = malloc(sizeof (*entry));
+ if (!entry)
+ return false;
+ entry->content = content;
+ entry->next = save_complete_list;
+ save_complete_list = entry;
+ return true;
+}
+
+
+/**
+ * Look up a url in the save_complete_list.
+ *
+ * \param url url to find
+ * \return content if found, 0 otherwise
+ */
+
+struct content * save_complete_list_find(const char *url)
+{
+ struct save_complete_entry *entry;
+ for (entry = save_complete_list; entry; entry = entry->next)
+ if (strcmp(url, entry->content->url) == 0)
+ return entry->content;
+ return 0;
+}
+
+
+/**
+ * Look up a content in the save_complete_list.
+ *
+ * \param content pointer to content
+ * \return true if the content is in the save_complete_list
+ */
+
+bool save_complete_list_check(struct content *content)
+{
+ struct save_complete_entry *entry;
+ for (entry = save_complete_list; entry; entry = entry->next)
+ if (entry->content == content)
+ return true;
+ return false;
+}
+
+
+#if 0
+/**
+ * Dump save complete list to stderr
+ */
+void save_complete_list_dump(void)
+{
+ struct save_complete_entry *entry;
+ for (entry = save_complete_list; entry; entry = entry->next)
+ fprintf(stderr, "%p : %s\n", entry->content,
+ entry->content->url);
+}
+#endif
+
+
+/**
+ * Create the inventory file listing original URLs.
+ */
+
+bool save_complete_inventory(const char *path)
+{
+ char spath[256];
+ FILE *fp;
+ char *pathstring;
+
+ pathstring = strdup("%s/Inventory");
+ if (!pathstring) {
+ warn_user("NoMemory", 0);
+ return false;
+ }
+ pathstring[2] = pathsep;
+ snprintf(spath, sizeof spath, pathstring, path);
+ free(pathstring);
+
+ fp = fopen(spath, "w");
+ if (!fp) {
+ LOG(("fopen(): errno = %i", errno));
+ warn_user("SaveError", strerror(errno));
+ return false;
+ }
+
+ struct save_complete_entry *entry;
+ for (entry = save_complete_list; entry; entry = entry->next)
+ fprintf(fp, "%x %s\n",
+ (unsigned int) entry->content,
+ entry->content->url);
+
+ fclose(fp);
+
+ return true;
+}
+
Index: desktop/save_complete.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ desktop/save_complete.h 2009-07-10 12:50:22.000000000 +0100
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2004 John M Bell <jmb202(a)ecs.soton.ac.uk>
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin(a)dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/ >.
+ */
+
+/** \file
+ * Save HTML document with dependencies (interface).
+ */
+
+#ifndef _NETSURF_DESKTOP_SAVE_COMPLETE_H_
+#define _NETSURF_DESKTOP_SAVE_COMPLETE_H_
+
+#include <stdbool.h>
+#include <libxml/HTMLtree.h>
+
+struct content;
+
+void save_complete_init(void);
+bool save_complete(struct content *c, const char *path);
+
+/**
+ * conducts the filesystem save appropriate to the gui
+ * \param path save path
+ * \param filename name of file to save
+ * \param c content to save, or NULL
+ * \param len data length
+ * \param sourcedata pointer to data to save, NULL when all data in sourcedata
+ * \param type integer filetype [riscos]
+ * \return true for success
+ */
+bool save_complete_gui_save(const char *path, const char *filename, struct content *c, int len, char *sourcedata, int type);
+
+/**
+ * wrapper for lib function htmlSaveFileFormat
+ */
+int save_complete_htmlSaveFileFormat(const char *path, const char *filename,
+ xmlDocPtr cur, const char *encoding, int format);
+
+/**
+ * mainly implemented as a wrapper for riscos
+ */
+bool save_complete_gui_filetype(const char *path, const char *filename,
+ int type);
+
+#endif
Changed files
!NetSurf/Resources/de/Messages | 113 ++
!NetSurf/Resources/en/Messages | 111 ++
!NetSurf/Resources/fr/Messages | 115 ++
!NetSurf/Resources/it/Messages | 146 ++-
!NetSurf/Resources/nl/Messages | 113 ++
Docs/Doxyfile | 2
Makefile | 8
Makefile.sources | 25
amiga/download.c | 2
amiga/gui.c | 6
amiga/menu.c | 2
amiga/save_complete.c | 827 +----------------
amiga/search.c | 668 +-------------
beos/beos_scaffolding.cpp | 4
content/content.c | 3
content/fetchcache.c | 97 +-
desktop/browser.c | 34
desktop/browser.h | 1
desktop/gui.h | 5
desktop/options.c | 8
desktop/options.h | 2
gtk/dialogs/gtk_options.c | 198 +++-
gtk/dialogs/gtk_options.h | 2
gtk/dialogs/gtk_source.c | 222 ++--
gtk/gtk_download.c | 4
gtk/gtk_gui.c | 114 +-
gtk/gtk_gui.h | 14
gtk/gtk_login.c | 2
gtk/gtk_scaffolding.c | 1952 +++++++++++++++++++++++++++--------------
gtk/gtk_scaffolding.h | 233 ++++
gtk/gtk_selection.c | 5
gtk/gtk_tabs.c | 34
gtk/gtk_window.c | 115 +-
gtk/gtk_window.h | 40
gtk/options.h | 10
gtk/res/netsurf.glade | 1661 ++--------------------------------
gtk/res/options.glade | 772 ++++++++++------
image/ico.c | 9
image/ico.h | 1
render/html.c | 8
render/html.h | 3
render/html_redraw.c | 2
render/textplain.c | 2
riscos/save.c | 64 +
riscos/search.c | 618 ------------
riscos/window.c | 13
utils/config.h | 5
utils/container.c | 170 ++-
utils/container.h | 3
utils/utils.c | 17
utils/utils.h | 1
51 files changed, 3855 insertions(+), 4731 deletions(-)
Index: render/html.c
===================================================================
--- render/html.c (revision 8438)
+++ render/html.c (working copy)
@@ -37,6 +37,7 @@
#include "desktop/options.h"
#include "image/bitmap.h"
#include "render/box.h"
+#include "render/favicon.h"
#include "render/font.h"
#include "render/form.h"
#include "render/html.h"
@@ -73,7 +74,6 @@
static void html_object_refresh(void *p);
static void html_destroy_frameset(struct content_html_frames *frameset);
static void html_destroy_iframe(struct content_html_iframe *iframe);
-static void html_set_status(struct content *c, const char *extra);
#if ALWAYS_DUMP_FRAMESET
static void html_dump_frameset(struct content_html_frames *frame,
unsigned int depth);
@@ -283,6 +283,7 @@
*
* - parsing to an XML tree is completed
* - stylesheets are fetched
+ * - favicon is retrieved
* - the XML tree is converted to a box tree and object fetches are started
* - the box tree is laid out
*
@@ -399,6 +400,9 @@
if (!html_find_stylesheets(c, html))
return false;
+ /* get icon */
+ favicon_get_icon(c, html);
+
/* Retrieve forms from parser */
c->data.html.forms = binding_get_forms(c->data.html.parser_binding);
for (f = c->data.html.forms; f != NULL; f = f->prev) {
@@ -765,7 +769,7 @@
* Uses STYLE and LINK elements inside and outside HEAD
*
* \param c content structure
- * \param head xml node of html element
+ * \param html xml node of html element
* \return true on success, false if an error occurred
*/
Index: render/html_redraw.c
===================================================================
--- render/html_redraw.c (revision 8438)
+++ render/html_redraw.c (working copy)
@@ -795,7 +795,7 @@
/* what about the current search operation, if any? */
if (!highlighted && search_current_window ==
- current_redraw_browser->window &&
+ current_redraw_browser &&
gui_search_term_highlighted(
current_redraw_browser->window,
offset, offset + len,
Index: render/textplain.c
===================================================================
--- render/textplain.c (revision 8438)
+++ render/textplain.c (working copy)
@@ -427,7 +427,7 @@
highlighted = true;
}
- if (!highlighted && search_current_window == bw->window) {
+ if (!highlighted && search_current_window == bw) {
unsigned start_idx, end_idx;
if (gui_search_term_highlighted(bw->window,
tab_ofst, tab_ofst + 1,
Index: render/html.h
===================================================================
--- render/html.h (revision 8438)
+++ render/html.h (working copy)
@@ -128,6 +128,8 @@
colour background_colour; /**< Document background colour. */
const struct font_functions *font_func;
+ struct content *favicon;
+
/** Number of entries in stylesheet_content. */
unsigned int stylesheet_count;
/** Stylesheets. Each may be 0. */
@@ -184,6 +186,7 @@
struct content *page, unsigned int index, struct box *box,
struct object_params *params);
void html_close(struct content *c);
+void html_set_status(struct content *c, const char *extra);
/* in render/html_redraw.c */
bool html_redraw(struct content *c, int x, int y,
Index: image/ico.c
===================================================================
--- image/ico.c (revision 8438)
+++ image/ico.c (working copy)
@@ -114,6 +114,15 @@
background_colour, BITMAPF_NONE);
}
+bool nsico_set_bitmap_from_size(struct content *c, int width, int height)
+{
+ struct bmp_image *bmp = ico_find(c->data.ico.ico, width, height);
+ if (!bmp->decoded)
+ if (bmp_decode(bmp) != BMP_OK)
+ return false;
+ c->bitmap = bmp->bitmap;
+ return true;
+}
bool nsico_redraw_tiled(struct content *c, int x, int y,
int width, int height,
Index: image/ico.h
===================================================================
--- image/ico.h (revision 8438)
+++ image/ico.h (working copy)
@@ -47,6 +47,7 @@
int clip_x0, int clip_y0, int clip_x1, int clip_y1,
float scale, colour background_colour,
bool repeat_x, bool repeat_y);
+bool nsico_set_bitmap_from_size(struct content *c, int width, int height);
#endif /* WITH_BMP */
Index: gtk/gtk_gui.h
===================================================================
--- gtk/gtk_gui.h (revision 8438)
+++ gtk/gtk_gui.h (working copy)
@@ -25,8 +25,18 @@
#include <glade/glade.h>
extern bool gui_in_multitask;
-extern GladeXML *gladeWindows;
-extern char *glade_file_location;
+extern GladeXML *gladeNetsurf;
+extern GladeXML *gladePassword;
+extern GladeXML *gladeWarning;
+extern GladeXML *gladeLogin;
+extern GladeXML *gladeSsl;
+extern char *glade_netsurf_file_location;
+extern char *glade_password_file_location;
+extern char *glade_warning_file_location;
+extern char *glade_login_file_location;
+extern char *glade_ssl_file_location;
+extern char *glade_toolbar_file_location;
+extern char *toolbar_indices_file_location;
extern char *options_file_location;
extern char *res_dir_location;
extern char *print_options_file_location;
Index: gtk/options.h
===================================================================
--- gtk/options.h (revision 8438)
+++ gtk/options.h (working copy)
@@ -34,6 +34,8 @@
extern bool option_hover_urls;
extern bool option_focus_new;
extern bool option_new_blank;
+extern bool option_source_tab;
+extern int option_current_theme;
#define EXTRA_OPTION_DEFINE \
bool option_render_resample = false; \
@@ -48,7 +50,9 @@
int option_history_age = 0; \
bool option_hover_urls = false; \
bool option_focus_new = false; \
-bool option_new_blank = false;
+bool option_new_blank = false; \
+bool option_source_tab = false;\
+int option_current_theme = 0;
#define EXTRA_OPTION_TABLE \
{ "render_resample", OPTION_BOOL, &option_render_resample }, \
@@ -63,6 +67,8 @@
{ "history_age", OPTION_INTEGER, &option_history_age}, \
{ "hover_urls", OPTION_BOOL, &option_hover_urls}, \
{ "focus_new", OPTION_BOOL, &option_focus_new}, \
-{ "new_blank", OPTION_BOOL, &option_new_blank}
+{ "new_blank", OPTION_BOOL, &option_new_blank}, \
+{ "source_tab", OPTION_BOOL, &option_source_tab},\
+{ "current_theme", OPTION_INTEGER, &option_current_theme}
#endif
Index: gtk/gtk_download.c
===================================================================
--- gtk/gtk_download.c (revision 8438)
+++ gtk/gtk_download.c (working copy)
@@ -33,6 +33,7 @@
#include "gtk/gtk_scaffolding.h"
#include "gtk/options.h"
#include "gtk/gtk_download.h"
+#include "gtk/gtk_window.h"
#define UPDATE_RATE 500 /* In milliseconds */
#define GLADE_NAME "downloads.glade"
@@ -206,7 +207,8 @@
messages_get("gtkUnknownSize") :
human_friendly_bytesize(total_size));
- nsgtk_download_parent = nsgtk_scaffolding_get_window(gui);
+ nsgtk_download_parent = nsgtk_scaffolding_window(nsgtk_get_scaffold(
+ gui));
struct gui_download_window *download = malloc(sizeof *download);
if (url_nice(url, &filename, false) != URL_FUNC_OK)
Index: gtk/gtk_window.c
===================================================================
--- gtk/gtk_window.c (revision 8438)
+++ gtk/gtk_window.c (working copy)
@@ -22,6 +22,7 @@
#include "gtk/gtk_window.h"
#include "desktop/browser.h"
#include "desktop/options.h"
+#include "desktop/searchweb.h"
#include "desktop/textinput.h"
#include "desktop/selection.h"
#include "gtk/gtk_gui.h"
@@ -35,6 +36,37 @@
#include <gdk/gdkkeysyms.h>
#include <assert.h>
+struct gui_window {
+ /* All gui_window objects have an ultimate scaffold */
+ nsgtk_scaffolding *scaffold;
+ /* A gui_window is the rendering of a browser_window */
+ struct browser_window *bw;
+ struct browser_mouse *mouse;
+
+ /* These are the storage for the rendering */
+ int caretx, carety, careth;
+ gui_pointer_shape current_pointer;
+ int last_x, last_y;
+
+ /* Within GTK, a gui_window is a scrolled window
+ * with a viewport inside
+ * with a gtkfixed in that
+ * with a drawing area in that
+ * The scrolled window is optional and only chosen
+ * for frames which need it. Otherwise we just use
+ * a viewport.
+ */
+ GtkWidget *tab;
+ GtkScrolledWindow *scrolledwindow;
+ GtkViewport *viewport;
+ GtkFixed *fixed;
+ GtkDrawingArea *drawing_area;
+ gulong signalhandler[2];
+
+ /* Keep gui_windows in a list for cleanup later */
+ struct gui_window *next, *prev;
+};
+
struct gui_window *window_list = 0; /**< first entry in win list*/
int temp_open_background = -1;
@@ -60,21 +92,47 @@
static GdkCursor *nsgtk_create_menu_cursor(void);
-struct browser_window *nsgtk_get_browser_window(struct gui_window *g)
+nsgtk_scaffolding *nsgtk_get_scaffold(struct gui_window *g)
{
+ return g->scaffold;
+}
+
+struct browser_window *gui_window_get_browser_window(struct gui_window *g)
+{
return g->bw;
}
-nsgtk_scaffolding *nsgtk_get_scaffold(struct gui_window *g)
+unsigned long nsgtk_window_get_signalhandler(struct gui_window *g, int i)
{
- return g->scaffold;
+ return g->signalhandler[i];
}
-struct browser_window *nsgtk_get_browser_for_gui(struct gui_window *g)
+GtkDrawingArea *nsgtk_window_get_drawing_area(struct gui_window *g)
{
- return g->bw;
+ return g->drawing_area;
}
+GtkScrolledWindow *nsgtk_window_get_scrolledwindow(struct gui_window *g)
+{
+ return g->scrolledwindow;
+}
+
+GtkWidget *nsgtk_window_get_tab(struct gui_window *g)
+{
+ return g->tab;
+}
+
+void nsgtk_window_set_tab(struct gui_window *g, GtkWidget *w)
+{
+ g->tab = w;
+}
+
+
+struct gui_window *nsgtk_window_iterate(struct gui_window *g)
+{
+ return g->next;
+}
+
float nsgtk_get_scale_for_gui(struct gui_window *g)
{
return g->bw->scale;
@@ -111,13 +169,6 @@
g->careth = 0;
- /* Attach ourselves to the list (push_top) */
- if (window_list)
- window_list->prev = g;
- g->next = window_list;
- g->prev = NULL;
- window_list = g;
-
if (bw->parent != NULL)
/* Find our parent's scaffolding */
g->scaffold = bw->parent->window->scaffold;
@@ -127,9 +178,20 @@
else
/* Now construct and attach a scaffold */
g->scaffold = nsgtk_new_scaffolding(g);
+ if (g->scaffold == NULL) {
+ free(g);
+ return NULL;
+ }
- /* Construct our primary elements */
- g->fixed = GTK_FIXED(gtk_fixed_new());
+ /* Attach ourselves to the list (push_top) */
+ if (window_list)
+ window_list->prev = g;
+ g->next = window_list;
+ g->prev = NULL;
+ window_list = g;
+
+ /* Construct our primary elements */
+ g->fixed = GTK_FIXED(gtk_fixed_new());
g->drawing_area = GTK_DRAWING_AREA(gtk_drawing_area_new());
gtk_fixed_put(g->fixed, GTK_WIDGET(g->drawing_area), 0, 0);
gtk_container_set_border_width(GTK_CONTAINER(g->fixed), 0);
@@ -229,17 +291,18 @@
#define CONNECT(obj, sig, callback, ptr) \
g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr))
- CONNECT(g->drawing_area, "expose_event", nsgtk_window_expose_event, g);
+ g->signalhandler[0] = CONNECT(g->drawing_area, "expose_event",
+ nsgtk_window_expose_event, g);
CONNECT(g->drawing_area, "motion_notify_event",
- nsgtk_window_motion_notify_event, g);
- CONNECT(g->drawing_area, "button_press_event",
- nsgtk_window_button_press_event, g);
+ nsgtk_window_motion_notify_event, g);
+ g->signalhandler[1] = CONNECT(g->drawing_area, "button_press_event",
+ nsgtk_window_button_press_event, g);
CONNECT(g->drawing_area, "button_release_event",
- nsgtk_window_button_release_event, g);
+ nsgtk_window_button_release_event, g);
CONNECT(g->drawing_area, "key_press_event",
- nsgtk_window_keypress_event, g);
+ nsgtk_window_keypress_event, g);
CONNECT(g->viewport, "size_allocate",
- nsgtk_window_size_allocate_event, g);
+ nsgtk_window_size_allocate_event, g);
return g;
}
@@ -382,6 +445,8 @@
struct gui_window *g = data;
gtk_widget_grab_focus(GTK_WIDGET(g->drawing_area));
+ gtk_widget_hide(GTK_WIDGET(nsgtk_scaffolding_history_window(
+ g->scaffold)->window));
g->mouse->pressed_x = event->x / g->bw->scale;
g->mouse->pressed_y = event->y / g->bw->scale;
@@ -562,7 +627,7 @@
{
for (struct gui_window *g = window_list; g; g = g->next) {
nsgtk_tab_options_changed(GTK_WIDGET(
- nsgtk_scaffolding_get_notebook(g)));
+ nsgtk_scaffolding_notebook(g->scaffold)));
g->bw->reformat_pending = true;
}
@@ -697,7 +762,13 @@
gtk_adjustment_set_value(hadj, x);
}
+void gui_window_scroll_visible(struct gui_window *g, int x0, int y0,
+ int x1, int y1)
+{
+ gui_window_set_scroll(g,x0,y0);
+}
+
/**
* Set the scale setting of a window
*
Index: gtk/gtk_window.h
===================================================================
--- gtk/gtk_window.h (revision 8438)
+++ gtk/gtk_window.h (working copy)
@@ -23,36 +23,6 @@
#include "desktop/browser.h"
#include "gtk/gtk_scaffolding.h"
-struct gui_window {
- /* All gui_window objects have an ultimate scaffold */
- nsgtk_scaffolding *scaffold;
- /* A gui_window is the rendering of a browser_window */
- struct browser_window *bw;
- struct browser_mouse *mouse;
-
- /* These are the storage for the rendering */
- int caretx, carety, careth;
- gui_pointer_shape current_pointer;
- int last_x, last_y;
-
- /* Within GTK, a gui_window is a scrolled window
- * with a viewport inside
- * with a gtkfixed in that
- * with a drawing area in that
- * The scrolled window is optional and only chosen
- * for frames which need it. Otherwise we just use
- * a viewport.
- */
- GtkWidget *tab;
- GtkScrolledWindow *scrolledwindow;
- GtkViewport *viewport;
- GtkFixed *fixed;
- GtkDrawingArea *drawing_area;
-
- /* Keep gui_windows in a list for cleanup later */
- struct gui_window *next, *prev;
-};
-
struct browser_mouse {
struct gui_window *gui;
struct box *box;
@@ -63,19 +33,23 @@
browser_mouse_state state;
};
-extern struct gui_window * window_list;
+extern struct gui_window *window_list;
extern int temp_open_background;
void nsgtk_reflow_all_windows(void);
void nsgtk_window_process_reformats(void);
nsgtk_scaffolding *nsgtk_get_scaffold(struct gui_window *g);
-struct browser_window *nsgtk_get_browser_for_gui(struct gui_window *g);
float nsgtk_get_scale_for_gui(struct gui_window *g);
int nsgtk_gui_window_update_targets(struct gui_window *g);
void nsgtk_window_destroy_browser(struct gui_window *g);
+unsigned long nsgtk_window_get_signalhandler(struct gui_window *g, int i);
+GtkDrawingArea *nsgtk_window_get_drawing_area(struct gui_window *g);
+struct gui_window *nsgtk_window_iterate(struct gui_window *g);
+GtkScrolledWindow *nsgtk_window_get_scrolledwindow(struct gui_window *g);
+GtkWidget *nsgtk_window_get_tab(struct gui_window *g);
+void nsgtk_window_set_tab(struct gui_window *g, GtkWidget *w);
-struct browser_window *nsgtk_get_browser_window(struct gui_window *g);
#endif /* NETSURF_GTK_WINDOW_H */
Index: gtk/gtk_scaffolding.c
===================================================================
--- gtk/gtk_scaffolding.c (revision 8438)
+++ gtk/gtk_scaffolding.c (working copy)
@@ -1,5 +1,6 @@
/*
* Copyright 2006 Rob Kendrick <rjek(a)rjek.com>
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin(a)dfgh.net>
*
* This file is part of NetSurf, http://www.netsurf-browser.org/
*
@@ -17,12 +18,16 @@
*/
#include <assert.h>
+#include <dirent.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <errno.h>
#include <gtk/gtk.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
#include <libxml/debugXML.h>
+#include "gtk/gtk_scaffolding.h"
#include "content/content.h"
#include "desktop/browser.h"
#include "desktop/history_core.h"
@@ -31,27 +36,36 @@
#include "desktop/options.h"
#include "desktop/plotters.h"
#include "desktop/print.h"
+#include "desktop/save_complete.h"
#ifdef WITH_PDF_EXPORT
#include "desktop/save_pdf/font_haru.h"
#include "desktop/save_pdf/pdf_plotters.h"
#endif
+#include "desktop/save_text.h"
+#include "desktop/search.h"
+#include "desktop/searchweb.h"
#include "desktop/selection.h"
#include "desktop/textinput.h"
#include "gtk/gtk_completion.h"
#include "gtk/dialogs/gtk_options.h"
#include "gtk/dialogs/gtk_about.h"
#include "gtk/dialogs/gtk_source.h"
+#include "gtk/gtk_bitmap.h"
#include "gtk/gtk_download.h"
#include "gtk/gtk_gui.h"
#include "gtk/gtk_history.h"
+#include "gtk/gtk_menu.h"
#include "gtk/gtk_plotters.h"
#include "gtk/gtk_print.h"
-#include "gtk/gtk_scaffolding.h"
#include "gtk/gtk_schedule.h"
+#include "gtk/gtk_search.h"
#include "gtk/gtk_tabs.h"
+#include "gtk/gtk_theme.h"
#include "gtk/gtk_throbber.h"
+#include "gtk/gtk_toolbar.h"
#include "gtk/gtk_window.h"
#include "gtk/options.h"
+#include "image/ico.h"
#include "render/box.h"
#include "render/font.h"
#include "render/form.h"
@@ -62,50 +76,85 @@
#include "utils/log.h"
-struct gtk_history_window;
+struct gtk_scaffolding {
+ GtkWindow *window;
+ GtkNotebook *notebook;
+ GtkEntry *url_bar;
+ GtkEntryCompletion *url_bar_completion;
+ GtkStatusbar *status_bar;
+ struct nsgtk_file_menu *file_menu;
+ struct nsgtk_file_menu *rclick_file_menu;
+ struct nsgtk_edit_menu *edit_menu;
+ struct nsgtk_edit_menu *rclick_edit_menu;
+ struct nsgtk_view_menu *view_menu;
+ struct nsgtk_view_menu *rclick_view_menu;
+ struct nsgtk_nav_menu *nav_menu;
+ struct nsgtk_nav_menu *rclick_nav_menu;
+ struct nsgtk_tabs_menu *tabs_menu;
+ struct nsgtk_tabs_menu *rclick_tabs_menu;
+ struct nsgtk_help_menu *help_menu;
+ struct nsgtk_help_menu *rclick_help_menu;
+ GtkMenuItem *edit_menu_item;
+ GtkMenuItem *tabs_menu_item;
+ GtkToolbar *tool_bar;
+ struct nsgtk_button_connect *buttons[PLACEHOLDER_BUTTON];
+ GtkMenuBar *menu_bar;
+ GtkImage *throbber;
+ GtkImage *icoFav;
+ struct gtk_search *search;
+ GtkImage *webSearchIco;
+ GtkEntry *webSearchEntry;
+ GtkPaned *status_pane;
+
+ int offset;
+ int toolbarmem;
+ int toolbarbase;
+ int historybase;
+
+ GladeXML *xml;
-struct gtk_history_window {
- struct gtk_scaffolding *g;
- GtkWindow *window;
- GtkScrolledWindow *scrolled;
- GtkDrawingArea *drawing_area;
-};
+ GladeXML *popup_xml;
+ GtkMenu *popup_menu;
-struct menu_events {
- const char *widget;
- GCallback handler;
+ struct gtk_history_window *history_window;
+ GtkDialog *preferences_dialog;
+
+ int throb_frame;
+ struct gui_window *top_level;
+ int being_destroyed;
+
+ bool fullscreen;
+
+ /* keep global linked list for gui interface adjustments */
+ struct gtk_scaffolding *next, *prev;
};
static int open_windows = 0; /**< current number of open browsers */
static struct gtk_scaffolding *current_model; /**< current window for model
- dialogue use */
+ dialogue use */
+nsgtk_scaffolding *scaf_list = NULL; /**< global list for interface changes */
static struct box *current_menu_link_box; /**< pointer to the box containing a
link under the mouse, or 0 if none */
-static gboolean nsgtk_window_delete_event(GtkWidget *, gpointer);
-static void nsgtk_window_destroy_event(GtkWidget *, gpointer);
+static gboolean nsgtk_window_delete_event(GtkWidget *, GdkEvent *, gpointer);
+static void nsgtk_window_close(struct gtk_scaffolding *g);
static void nsgtk_window_update_back_forward(struct gtk_scaffolding *);
static void nsgtk_throb(void *);
+static gboolean nsgtk_filter_directory(const GtkFileFilterInfo *info,
+ gpointer data);
static gboolean nsgtk_window_edit_menu_clicked(GtkWidget *widget,
struct gtk_scaffolding *g);
static gboolean nsgtk_window_edit_menu_hidden(GtkWidget *widget,
struct gtk_scaffolding *g);
static gboolean nsgtk_window_popup_menu_hidden(GtkWidget *widget,
struct gtk_scaffolding *g);
-static gboolean nsgtk_window_back_button_clicked(GtkWidget *, gpointer);
-static gboolean nsgtk_window_history_button_clicked(GtkWidget *, gpointer);
-static gboolean nsgtk_window_forward_button_clicked(GtkWidget *, gpointer);
-static gboolean nsgtk_window_stop_button_clicked(GtkWidget *, gpointer);
-static gboolean nsgtk_window_reload_button_clicked(GtkWidget *, gpointer);
-static gboolean nsgtk_window_home_button_clicked(GtkWidget *, gpointer);
-static gboolean nsgtk_window_url_activate_event(GtkWidget *, gpointer);
-static gboolean nsgtk_window_url_changed(GtkWidget *, GdkEventKey *, gpointer);
-
+static gboolean nsgtk_window_tool_bar_clicked(GtkToolbar *toolbar, gint x,
+ gint y, gint button, gpointer data);
static guint nsgtk_scaffolding_update_link_operations_sensitivity(
struct gtk_scaffolding *g, GladeXML *xml, gdouble x, gdouble y,
gboolean hide);
static guint nsgtk_scaffolding_update_edit_actions_sensitivity(
- struct gtk_scaffolding *g, GladeXML *xml, gboolean hide);
+ struct gtk_scaffolding *g, GladeXML *xml, bool hide);
static void nsgtk_scaffolding_enable_link_operations_sensitivity(
struct gtk_scaffolding *g, GladeXML *xml);
static void nsgtk_scaffolding_enable_edit_actions_sensitivity(
@@ -116,168 +165,70 @@
static gboolean nsgtk_history_button_press_event(GtkWidget *, GdkEventButton *,
gpointer);
-static void nsgtk_attach_menu_handlers(GladeXML *, gpointer);
+static void nsgtk_attach_menu_handlers(struct gtk_scaffolding *);
static void nsgtk_window_tabs_num_changed(GtkNotebook *notebook,
GtkWidget *page, guint page_num, struct gtk_scaffolding *g);
void nsgtk_openfile_open(const char *filename);
-#define MENUEVENT(x) { #x, G_CALLBACK(nsgtk_on_##x##_activate) }
-#define MENUPROTO(x) static gboolean nsgtk_on_##x##_activate( \
- GtkMenuItem *widget, gpointer g)
-/* prototypes for menu handlers */
-/* file menu */
-MENUPROTO(new_window);
-MENUPROTO(new_tab);
-MENUPROTO(open_location);
-MENUPROTO(open_file);
-#ifdef WITH_PDF_EXPORT
-MENUPROTO(export_pdf);
-#endif
-MENUPROTO(print);
-MENUPROTO(close_window);
-MENUPROTO(quit);
-
-/* edit menu */
-MENUPROTO(cut);
-MENUPROTO(copy);
-MENUPROTO(paste);
-MENUPROTO(select_all);
-MENUPROTO(preferences);
-
-/* view menu */
-MENUPROTO(stop);
-MENUPROTO(reload);
-MENUPROTO(zoom_in);
-MENUPROTO(normal_size);
-MENUPROTO(zoom_out);
-MENUPROTO(full_screen);
-MENUPROTO(view_source);
-MENUPROTO(menu_bar);
-MENUPROTO(tool_bar);
-MENUPROTO(status_bar);
-MENUPROTO(downloads);
-MENUPROTO(save_window_size);
-MENUPROTO(toggle_debug_rendering);
-MENUPROTO(save_box_tree);
-MENUPROTO(save_dom_tree);
-
-/* navigate menu */
-MENUPROTO(back);
-MENUPROTO(forward);
-MENUPROTO(home);
-MENUPROTO(local_history);
-MENUPROTO(global_history);
-
-/* tabs menu */
-MENUPROTO(next_tab);
-MENUPROTO(prev_tab);
-MENUPROTO(close_tab);
-
-/* help menu */
-MENUPROTO(about);
-
-/* Popup context menu (also shares edit menu handlers) */
-MENUPROTO(save_link);
-MENUPROTO(open_link_in_focused_tab);
-MENUPROTO(open_link_in_background_tab);
-
-/* structure used by nsgtk_attach_menu_handlers to connect menu items to
- * their handling functions.
- */
-static struct menu_events menu_events[] = {
- /* file menu */
- MENUEVENT(new_window),
- MENUEVENT(new_tab),
- MENUEVENT(open_location),
- MENUEVENT(open_file),
-#ifdef WITH_PDF_EXPORT
- MENUEVENT(export_pdf),
-#endif
- MENUEVENT(print),
- MENUEVENT(close_window),
- MENUEVENT(quit),
-
- /* edit menu */
- MENUEVENT(cut),
- MENUEVENT(copy),
- MENUEVENT(paste),
- MENUEVENT(select_all),
- MENUEVENT(preferences),
-
- /* view menu */
- MENUEVENT(stop),
- MENUEVENT(reload),
- MENUEVENT(zoom_in),
- MENUEVENT(normal_size),
- MENUEVENT(zoom_out),
- MENUEVENT(full_screen),
- MENUEVENT(view_source),
- MENUEVENT(menu_bar),
- MENUEVENT(tool_bar),
- MENUEVENT(status_bar),
- MENUEVENT(downloads),
- MENUEVENT(save_window_size),
- MENUEVENT(toggle_debug_rendering),
- MENUEVENT(save_box_tree),
- MENUEVENT(save_dom_tree),
-
- /* navigate menu */
- MENUEVENT(back),
- MENUEVENT(forward),
- MENUEVENT(home),
- MENUEVENT(local_history),
- MENUEVENT(global_history),
-
- /* tab menu */
- MENUEVENT(next_tab),
- MENUEVENT(prev_tab),
- MENUEVENT(close_tab),
-
- /* help menu */
- MENUEVENT(about),
-
- /* sentinel */
- { NULL, NULL }
-};
-
-void nsgtk_attach_menu_handlers(GladeXML *xml, gpointer g)
+void nsgtk_attach_menu_handlers(struct gtk_scaffolding *g)
{
- struct menu_events *event = menu_events;
-
- while (event->widget != NULL)
- {
- GtkWidget *w = glade_xml_get_widget(xml, event->widget);
- g_signal_connect(G_OBJECT(w), "activate", event->handler, g);
- event++;
+ for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ if (g->buttons[i]->main != NULL) {
+ g_signal_connect(g->buttons[i]->main, "activate",
+ G_CALLBACK(g->buttons[i]->mhandler), g);
+ }
+ if (g->buttons[i]->rclick != NULL) {
+ g_signal_connect(g->buttons[i]->rclick, "activate",
+ G_CALLBACK(g->buttons[i]->mhandler), g);
+ }
+ if (g->buttons[i]->popup != NULL) {
+ g_signal_connect(g->buttons[i]->popup, "activate",
+ G_CALLBACK(g->buttons[i]->mhandler), g);
+ }
}
-}
+#define CONNECT_CHECK(q)\
+ g_signal_connect(g->view_menu->toolbars_submenu->q##_menuitem,\
+ "toggled", G_CALLBACK(nsgtk_on_##q##_activate), g);\
+ g_signal_connect(g->rclick_view_menu->toolbars_submenu->q##_menuitem,\
+ "toggled", G_CALLBACK(nsgtk_on_##q##_activate), g)
+ CONNECT_CHECK(menubar);
+ CONNECT_CHECK(toolbar);
+ CONNECT_CHECK(statusbar);
+#undef CONNECT_CHECK
-GtkWindow *nsgtk_get_window_for_scaffold(struct gtk_scaffolding *g)
-{
- return g->window;
}
/* event handlers and support functions for them */
-gboolean nsgtk_window_delete_event(GtkWidget *widget, gpointer data)
+gboolean nsgtk_window_delete_event(GtkWidget *widget, GdkEvent *event, gpointer
+ data)
{
- struct gtk_scaffolding *g = data;
- gtk_widget_destroy(GTK_WIDGET(g->window));
- if (open_windows == 1 && nsgtk_check_for_downloads(GTK_WINDOW(widget)))
- return TRUE;
- else
- return FALSE;
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
+ if (open_windows != 1 || nsgtk_check_for_downloads(GTK_WINDOW(
+ widget)) == false) {
+ nsgtk_window_close(g);
+ gtk_widget_destroy(GTK_WIDGET(g->window));
+ }
+ return TRUE;
}
-void nsgtk_window_destroy_event(GtkWidget *widget, gpointer data)
+/**
+ * cleanup function
+ */
+void nsgtk_window_close(struct gtk_scaffolding *g)
{
- struct gtk_scaffolding *g = data;
+ /* close all tabs first */
+ gint numbertabs = gtk_notebook_get_n_pages(g->notebook);
+ while (numbertabs-- > 1) {
+ nsgtk_tab_close_current(g->notebook);
+ }
LOG(("Being Destroyed = %d", g->being_destroyed));
- if (g->history_window->window) {
+/* if ((g->history_window) && (g->history_window->window)) {
gtk_widget_destroy(GTK_WIDGET(g->history_window->window));
}
- gtk_widget_destroy(GTK_WIDGET(g->window));
-
+ if (g->window)
+ gtk_widget_destroy(GTK_WIDGET(g->window));
+*/
if (--open_windows == 0)
netsurf_quit = true;
@@ -285,38 +236,38 @@
g->being_destroyed = 1;
nsgtk_window_destroy_browser(g->top_level);
}
+ if (g->prev)
+ g->prev->next = g->next;
+ else
+ scaf_list = g->next;
+
+ if (g->next)
+ g->next->prev = g->prev;
+
}
-void nsgtk_scaffolding_destroy(nsgtk_scaffolding *scaffold)
+void nsgtk_scaffolding_destroy(nsgtk_scaffolding *g)
{
/* Our top_level has asked us to die */
- LOG(("Being Destroyed = %d", scaffold->being_destroyed));
- if (scaffold->being_destroyed) return;
- scaffold->being_destroyed = 1;
- nsgtk_window_destroy_event(0, scaffold);
+ LOG(("Being Destroyed = %d", g->being_destroyed));
+ if (g->being_destroyed) return;
+ g->being_destroyed = 1;
+ nsgtk_window_close(g);
}
void nsgtk_window_update_back_forward(struct gtk_scaffolding *g)
{
int width, height;
- struct browser_window *bw = nsgtk_get_browser_for_gui(g->top_level);
+ struct browser_window *bw = gui_window_get_browser_window(g->top_level);
- gtk_widget_set_sensitive(GTK_WIDGET(g->back_button),
- history_back_available(bw->history));
- gtk_widget_set_sensitive(GTK_WIDGET(g->forward_button),
- history_forward_available(bw->history));
+ g->buttons[BACK_BUTTON]->sensitivity =
+ history_back_available(bw->history);
+ g->buttons[FORWARD_BUTTON]->sensitivity = history_forward_available(
+ bw->history);
+
+ nsgtk_scaffolding_set_sensitivity(g);
- gtk_widget_set_sensitive(GTK_WIDGET(g->back_menu),
- history_back_available(bw->history));
- gtk_widget_set_sensitive(GTK_WIDGET(g->forward_menu),
- history_forward_available(bw->history));
- gtk_widget_set_sensitive(GTK_WIDGET(glade_xml_get_widget(g->popup_xml,
- "popupBack")), history_back_available(bw->history));
- gtk_widget_set_sensitive(GTK_WIDGET(glade_xml_get_widget(g->popup_xml,
- "popupForward")),
- history_forward_available(bw->history));
-
/* update the url bar, particularly necessary when tabbing */
if (bw->current_content != NULL && bw->current_content->url != NULL)
browser_window_refresh_url_bar(bw, bw->current_content->url,
@@ -350,7 +301,7 @@
static gboolean nsgtk_window_edit_menu_clicked(GtkWidget *widget,
struct gtk_scaffolding *g)
{
- nsgtk_scaffolding_update_edit_actions_sensitivity (g, g->xml, FALSE);
+ nsgtk_scaffolding_update_edit_actions_sensitivity(g, g->xml, false);
return TRUE;
}
@@ -370,97 +321,15 @@
return TRUE;
}
-gboolean nsgtk_window_back_button_clicked(GtkWidget *widget, gpointer data)
-{
- struct gtk_scaffolding *g = data;
- struct browser_window *bw = nsgtk_get_browser_for_gui(g->top_level);
-
- if (!history_back_available(bw->history))
- return TRUE;
-
- history_back(bw, bw->history);
- nsgtk_window_update_back_forward(g);
-
- return TRUE;
-}
-
-/* TODO: add resize handling */
-gboolean nsgtk_window_history_button_clicked(GtkWidget *widget, gpointer data)
-{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)data;
-
- /* if entries of the same url but different frag_ids have been added
- * the history needs redrawing (what is done in the throbber code in
- * other cases)
- */
- nsgtk_window_update_back_forward(gw);
-
- gtk_window_set_default_size(gw->history_window->window, 500, 150);
- gtk_window_set_position(gw->history_window->window, GTK_WIN_POS_MOUSE);
- gtk_window_set_transient_for(gw->history_window->window, gw->window);
- gtk_window_set_opacity(gw->history_window->window, 0.9);
- gtk_widget_show(GTK_WIDGET(gw->history_window->window));
- gdk_window_raise(GTK_WIDGET(gw->history_window->window)->window);
-
- return TRUE;
-}
-
-gboolean nsgtk_window_forward_button_clicked(GtkWidget *widget, gpointer data)
-{
- struct gtk_scaffolding *g = data;
- struct browser_window *bw = nsgtk_get_browser_for_gui(g->top_level);
-
- if (!history_forward_available(bw->history))
- return TRUE;
-
- history_forward(bw, bw->history);
- nsgtk_window_update_back_forward(g);
-
- return TRUE;
-}
-
-gboolean nsgtk_window_stop_button_clicked(GtkWidget *widget, gpointer data)
-{
- struct gtk_scaffolding *g = data;
- struct browser_window *bw = nsgtk_get_browser_for_gui(g->top_level);
-
- browser_window_stop(bw);
-
- return TRUE;
-}
-
-gboolean nsgtk_window_reload_button_clicked(GtkWidget *widget, gpointer data)
-{
- struct gtk_scaffolding *g = data;
- struct browser_window *bw = nsgtk_get_browser_for_gui(g->top_level);
-
- browser_window_reload(bw, true);
-
- return TRUE;
-}
-
-gboolean nsgtk_window_home_button_clicked(GtkWidget *widget, gpointer data)
-{
- struct gtk_scaffolding *g = data;
- static const char *addr = NETSURF_HOMEPAGE;
- struct browser_window *bw = nsgtk_get_browser_for_gui(g->top_level);
-
- if (option_homepage_url != NULL && option_homepage_url[0] != '\0')
- addr = option_homepage_url;
-
- browser_window_go(bw, addr, 0, true);
-
- return TRUE;
-}
-
gboolean nsgtk_window_url_activate_event(GtkWidget *widget, gpointer data)
{
struct gtk_scaffolding *g = data;
- struct browser_window *bw = nsgtk_get_browser_for_gui(g->top_level);
+ struct browser_window *bw = gui_window_get_browser_window(g->top_level);
+ char *url = (char *)gtk_entry_get_text(GTK_ENTRY(g->url_bar));
+ if (!search_is_url(url))
+ url = search_web_from_term(url);
+ browser_window_go(bw, url, 0, true);
- browser_window_go(bw, gtk_entry_get_text(GTK_ENTRY(g->url_bar)),
- 0, true);
-
return TRUE;
}
@@ -476,17 +345,40 @@
return TRUE;
}
+gboolean nsgtk_window_tool_bar_clicked(GtkToolbar *toolbar, gint x, gint y,
+ gint button, gpointer data)
+{
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
+ /* set visibility for right-click menu */
+ gtk_widget_hide(glade_xml_get_widget(g->popup_xml, "sep2"));
+ gtk_widget_hide(glade_xml_get_widget(g->popup_xml, "save_link_popup"));
+ gtk_widget_hide(glade_xml_get_widget(g->popup_xml,
+ "open_link_in_focused_tab_popup"));
+ gtk_widget_hide(glade_xml_get_widget(g->popup_xml,
+ "open_link_in_background_tab_popup"));
+ gtk_widget_show(glade_xml_get_widget(g->popup_xml, "customize_popup"));
+ gtk_widget_hide(glade_xml_get_widget(g->popup_xml, "copy_popup"));
+ gtk_widget_hide(glade_xml_get_widget(g->popup_xml, "cut_popup"));
+ gtk_widget_hide(glade_xml_get_widget(g->popup_xml, "paste_popup"));
+ gtk_menu_popup(g->popup_menu, NULL, NULL, NULL, NULL, 0,
+ gtk_get_current_event_time());
+ return TRUE;
+}
void nsgtk_window_tabs_num_changed(GtkNotebook *notebook, GtkWidget *page,
guint page_num, struct gtk_scaffolding *g)
{
gboolean visible = gtk_notebook_get_show_tabs(g->notebook);
- g_object_set(g->tabs_menu, "visible", visible, NULL);
+ g_object_set(g->tabs_menu_item, "visible", visible, NULL);
+ g->buttons[NEXTTAB_BUTTON]->sensitivity = visible;
+ g->buttons[PREVTAB_BUTTON]->sensitivity = visible;
+ g->buttons[CLOSETAB_BUTTON]->sensitivity = visible;
+ nsgtk_scaffolding_set_sensitivity(g);
}
void nsgtk_openfile_open(const char *filename)
{
- struct browser_window *bw = nsgtk_get_browser_for_gui(
+ struct browser_window *bw = gui_window_get_browser_window(
current_model->top_level);
char *url = malloc(strlen(filename) + sizeof("file://"));
@@ -498,25 +390,39 @@
}
/* signal handlers for menu entries */
-#define MENUHANDLER(x) gboolean nsgtk_on_##x##_activate(GtkMenuItem *widget, \
- gpointer g)
+#define MULTIHANDLER(q)\
+gboolean nsgtk_on_##q##_activate_menu(GtkMenuItem *widget, gpointer data)\
+{\
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;\
+ return nsgtk_on_##q##_activate(g);\
+}\
+gboolean nsgtk_on_##q##_activate_button(GtkButton *widget, gpointer data)\
+{\
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;\
+ return nsgtk_on_##q##_activate(g);\
+}\
+gboolean nsgtk_on_##q##_activate(struct gtk_scaffolding *g)
-MENUHANDLER(new_window)
+#define MENUHANDLER(q)\
+gboolean nsgtk_on_##q##_activate(GtkMenuItem *widget, gpointer data)
+
+#define BUTTONHANDLER(q)\
+gboolean nsgtk_on_##q##_activate(GtkButton *widget, gpointer data)
+
+MULTIHANDLER(newwindow)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
- struct browser_window *bw = nsgtk_get_browser_for_gui(gw->top_level);
- const char *url = gtk_entry_get_text(GTK_ENTRY(gw->url_bar));
+ struct browser_window *bw = gui_window_get_browser_window(g->top_level);
+ const char *url = gtk_entry_get_text(GTK_ENTRY(g->url_bar));
browser_window_create(url, bw, NULL, false, false);
return TRUE;
}
-MENUHANDLER(new_tab)
+MULTIHANDLER(newtab)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
- struct browser_window *bw = nsgtk_get_browser_for_gui(gw->top_level);
- const char *url = gtk_entry_get_text(GTK_ENTRY(gw->url_bar));
+ struct browser_window *bw = gui_window_get_browser_window(g->top_level);
+ const char *url = gtk_entry_get_text(GTK_ENTRY(g->url_bar));
if (option_new_blank)
browser_window_create(0, bw, NULL, false, true);
@@ -527,18 +433,16 @@
return TRUE;
}
-MENUHANDLER(open_location)
+MULTIHANDLER(open_location)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
+ gtk_widget_grab_focus(GTK_WIDGET(g->url_bar));
- gtk_widget_grab_focus(GTK_WIDGET(gw->url_bar));
-
return TRUE;
}
-MENUHANDLER(open_file)
+MULTIHANDLER(openfile)
{
- current_model = (struct gtk_scaffolding *)g;
+ current_model = g;
GtkWidget *dlgOpen = gtk_file_chooser_dialog_new("Open File",
current_model->window, GTK_FILE_CHOOSER_ACTION_OPEN,
GTK_STOCK_CANCEL, -6, GTK_STOCK_OPEN, -5, NULL);
@@ -557,12 +461,75 @@
return TRUE;
}
+MULTIHANDLER(savepage)
+{
+ if (gui_window_get_browser_window(g->top_level)->current_content
+ == NULL)
+ return FALSE;
+
+ GtkWidget *fc = gtk_file_chooser_dialog_new(
+ messages_get("gtkcompleteSave"), g->window,
+ GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_APPLY, GTK_RESPONSE_ACCEPT,
+ NULL);
+ DIR *d;
+ char *path;
+ url_func_result res;
+ GtkFileFilter *filter = gtk_file_filter_new();
+ gtk_file_filter_set_name(filter, "directory");
+ gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME,
+ nsgtk_filter_directory, NULL, NULL);
+ gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fc), filter);
+ gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(fc), filter);
+
+ res = url_nice(gui_window_get_browser_window(
+ g->top_level)->current_content->url, &path, false);
+ if (res != URL_FUNC_OK) {
+ path = strdup(messages_get("SaveText"));
+ if (path == NULL) {
+ warn_user("NoMemory", 0);
+ return FALSE;
+ }
+ }
+
+ if (access(path, F_OK) != 0)
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fc), path);
+ free(path);
+
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(fc),
+ TRUE);
+
+ if (gtk_dialog_run(GTK_DIALOG(fc)) != GTK_RESPONSE_ACCEPT)
+ return TRUE;
+ path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
+ d = opendir(path);
+ if (d == NULL) {
+ printf("d NULL\n");
+ if (errno == ENOTDIR)
+ warn_user("NoDirError", path);
+ else
+ warn_user("gtkFileError", path);
+ return TRUE;
+ }
+ closedir(d);
+ save_complete_init();
+ save_complete(gui_window_get_browser_window(
+ g->top_level)->current_content, path);
+ g_free(path);
+
+ gtk_widget_destroy(fc);
+
+ return TRUE;
+}
+
+
+MULTIHANDLER(pdf)
+{
#ifdef WITH_PDF_EXPORT
-MENUHANDLER(export_pdf)
-{
+
GtkWidget *save_dialog;
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
- struct browser_window *bw = nsgtk_get_browser_for_gui(gw->top_level);
+ struct browser_window *bw = gui_window_get_browser_window(g->top_level);
struct print_settings *settings;
char filename[PATH_MAX];
char dirname[PATH_MAX];
@@ -592,7 +559,7 @@
used by the all-purpose print interface*/
haru_nsfont_set_scale((float)option_export_scale / 100);
- save_dialog = gtk_file_chooser_dialog_new("Export to PDF", gw->window,
+ save_dialog = gtk_file_chooser_dialog_new("Export to PDF", g->window,
GTK_FILE_CHOOSER_ACTION_SAVE,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
@@ -624,14 +591,72 @@
gtk_widget_destroy(save_dialog);
+#endif /* WITH_PDF_EXPORT */
+
return TRUE;
}
-#endif /* WITH_PDF_EXPORT */
-MENUHANDLER(print)
+MULTIHANDLER(plaintext)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
- struct browser_window *bw = nsgtk_get_browser_for_gui(gw->top_level);
+ if (gui_window_get_browser_window(g->top_level)->current_content
+ == NULL)
+ return FALSE;
+
+ GtkWidget *fc = gtk_file_chooser_dialog_new(
+ messages_get("gtkplainSave"), g->window,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+ char *filename;
+ url_func_result res;
+
+ res = url_nice(gui_window_get_browser_window(
+ g->top_level)->current_content->url, &filename, false);
+ if (res != URL_FUNC_OK) {
+ filename = strdup(messages_get("SaveText"));
+ if (filename == NULL) {
+ warn_user("NoMemory", 0);
+ return FALSE;
+ }
+ }
+
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fc), filename);
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(fc),
+ TRUE);
+
+ free(filename);
+
+ if (gtk_dialog_run(GTK_DIALOG(fc)) == GTK_RESPONSE_ACCEPT) {
+ filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
+ save_as_text(gui_window_get_browser_window(
+ g->top_level)->current_content, filename);
+ g_free(filename);
+ }
+
+ gtk_widget_destroy(fc);
+ return TRUE;
+}
+
+MULTIHANDLER(drawfile)
+{
+ return TRUE;
+}
+
+MULTIHANDLER(postscript)
+{
+ return TRUE;
+}
+
+MULTIHANDLER(printpreview)
+{
+ return TRUE;
+}
+
+
+MULTIHANDLER(print)
+{
+ struct browser_window *bw = gui_window_get_browser_window(g->top_level);
GtkPrintOperation *print_op;
GtkPageSetup *page_setup;
@@ -658,7 +683,7 @@
content_to_print = bw->current_content;
- page_setup = gtk_print_run_page_setup_dialog(gw->window, NULL, NULL);
+ page_setup = gtk_print_run_page_setup_dialog(g->window, NULL, NULL);
if (page_setup == NULL) {
warn_user(messages_get("NoMemory"), 0);
g_object_unref(print_op);
@@ -677,7 +702,7 @@
if (bw->current_content->type != CONTENT_TEXTPLAIN)
res = gtk_print_operation_run(print_op,
GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
- gw->window,
+ g->window,
NULL);
/* if the settings were used save them for future use */
@@ -697,50 +722,46 @@
return TRUE;
}
-MENUHANDLER(close_window)
+MULTIHANDLER(closewindow)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
-
/* close all tabs first */
- gint numbertabs = gtk_notebook_get_n_pages(gw->notebook);
+ gint numbertabs = gtk_notebook_get_n_pages(g->notebook);
while (numbertabs-- > 1) {
- nsgtk_tab_close_current(gw->notebook);
+ nsgtk_tab_close_current(g->notebook);
}
- gtk_widget_destroy(GTK_WIDGET(gw->window));
-
+ nsgtk_window_close(g);
+ gtk_widget_destroy(GTK_WIDGET(g->window));
return TRUE;
}
-MENUHANDLER(quit)
+MULTIHANDLER(quit)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
-
- if (nsgtk_check_for_downloads(gw->window) == false)
+ if (nsgtk_check_for_downloads(g->window) == false)
netsurf_quit = true;
return TRUE;
}
-MENUHANDLER(save_link)
+MENUHANDLER(savelink)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
- struct gui_window *gui = gw->top_level;
- struct browser_window *bw = nsgtk_get_browser_for_gui(gui);
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *) data;
+ struct gui_window *gui = g->top_level;
+ struct browser_window *bw = gui_window_get_browser_window(gui);
+
+ if (!current_menu_link_box)
+ return FALSE;
+
+ browser_window_download(bw, current_menu_link_box->href,
+ bw->current_content->url);
- if (!current_menu_link_box)
- return FALSE;
-
- browser_window_download(bw, current_menu_link_box->href,
- bw->current_content->url);
-
- return TRUE;
+ return TRUE;
}
-MENUHANDLER(open_link_in_focused_tab)
+MENUHANDLER(linkfocused)
{
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *) data;
temp_open_background = 0;
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
- struct gui_window *gui = gw->top_level;
- struct browser_window *bw = nsgtk_get_browser_for_gui(gui);
+ struct gui_window *gui = g->top_level;
+ struct browser_window *bw = gui_window_get_browser_window(gui);
if (current_menu_link_box == NULL)
return FALSE;
@@ -752,11 +773,11 @@
return TRUE;
}
-MENUHANDLER(open_link_in_background_tab)
+MENUHANDLER(linkbackground)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
- struct gui_window *gui = gw->top_level;
- struct browser_window *bw = nsgtk_get_browser_for_gui(gui);
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *) data;
+ struct gui_window *gui = g->top_level;
+ struct browser_window *bw = gui_window_get_browser_window(gui);
temp_open_background = 1;
@@ -771,41 +792,38 @@
}
-MENUHANDLER(cut)
+MULTIHANDLER(cut)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
- struct browser_window *bw = nsgtk_get_browser_for_gui(gw->top_level);
- GtkWidget *focused = gtk_window_get_focus(gw->window);
+ struct browser_window *bw = gui_window_get_browser_window(g->top_level);
+ GtkWidget *focused = gtk_window_get_focus(g->window);
/* If the url bar has focus, let gtk handle it */
if (GTK_IS_EDITABLE (focused))
- gtk_editable_cut_clipboard (GTK_EDITABLE(gw->url_bar));
+ gtk_editable_cut_clipboard (GTK_EDITABLE(g->url_bar));
else
browser_window_key_press(bw, KEY_CUT_SELECTION);
return TRUE;
}
-MENUHANDLER(copy)
+MULTIHANDLER(copy)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
- struct browser_window *bw = nsgtk_get_browser_for_gui(gw->top_level);
- GtkWidget *focused = gtk_window_get_focus(gw->window);
+ struct browser_window *bw = gui_window_get_browser_window(g->top_level);
+ GtkWidget *focused = gtk_window_get_focus(g->window);
/* If the url bar has focus, let gtk handle it */
if (GTK_IS_EDITABLE (focused))
- gtk_editable_copy_clipboard(GTK_EDITABLE(gw->url_bar));
+ gtk_editable_copy_clipboard(GTK_EDITABLE(g->url_bar));
else
gui_copy_to_clipboard(bw->sel);
return TRUE;
}
-MENUHANDLER(paste)
+MULTIHANDLER(paste)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
- struct gui_window *gui = gw->top_level;
- GtkWidget *focused = gtk_window_get_focus(gw->window);
+ struct gui_window *gui = g->top_level;
+ GtkWidget *focused = gtk_window_get_focus(g->window);
/* If the url bar has focus, let gtk handle it */
if (GTK_IS_EDITABLE (focused))
@@ -816,14 +834,25 @@
return TRUE;
}
-MENUHANDLER(select_all)
+MULTIHANDLER(delete)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
- struct browser_window *bw = nsgtk_get_browser_for_gui(gw->top_level);
+ return TRUE;
+}
+
+MENUHANDLER(customize)
+{
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
+ nsgtk_toolbar_customization_init(g);
+ return TRUE;
+}
+
+MULTIHANDLER(selectall)
+{
+ struct browser_window *bw = gui_window_get_browser_window(g->top_level);
- if (GTK_WIDGET_HAS_FOCUS(gw->url_bar)) {
+ if (GTK_WIDGET_HAS_FOCUS(g->url_bar)) {
LOG(("Selecting all URL bar text"));
- gtk_editable_select_region(GTK_EDITABLE(gw->url_bar), 0, -1);
+ gtk_editable_select_region(GTK_EDITABLE(g->url_bar), 0, -1);
} else {
LOG(("Selecting all document text"));
selection_select_all(bw->sel);
@@ -832,145 +861,211 @@
return TRUE;
}
-MENUHANDLER(preferences)
+MULTIHANDLER(find)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
- struct browser_window *bw = nsgtk_get_browser_for_gui(gw->top_level);
- if (gw->preferences_dialog == NULL)
- gw->preferences_dialog = nsgtk_options_init(bw, gw->window);
+ nsgtk_scaffolding_toggle_search_bar_visibility(g);
+ return TRUE;
+}
+
+MULTIHANDLER(preferences)
+{
+ struct browser_window *bw = gui_window_get_browser_window(g->top_level);
+ if (g->preferences_dialog == NULL)
+ g->preferences_dialog = nsgtk_options_init(bw, g->window);
else
- gtk_widget_show (GTK_WIDGET(gw->preferences_dialog));
+ gtk_widget_show(GTK_WIDGET(g->preferences_dialog));
return TRUE;
}
-MENUHANDLER(zoom_in)
+MULTIHANDLER(zoomplus)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
- struct browser_window *bw = nsgtk_get_browser_for_gui(gw->top_level);
- float old_scale = nsgtk_get_scale_for_gui(gw->top_level);
+ struct browser_window *bw = gui_window_get_browser_window(g->top_level);
+ float old_scale = nsgtk_get_scale_for_gui(g->top_level);
browser_window_set_scale(bw, old_scale + 0.05, true);
return TRUE;
}
-MENUHANDLER(normal_size)
+MULTIHANDLER(zoomnormal)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
- struct browser_window *bw = nsgtk_get_browser_for_gui(gw->top_level);
+ struct browser_window *bw = gui_window_get_browser_window(g->top_level);
browser_window_set_scale(bw, 1.0, true);
return TRUE;
}
-MENUHANDLER(zoom_out)
+MULTIHANDLER(zoomminus)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
- struct browser_window *bw = nsgtk_get_browser_for_gui(gw->top_level);
- float old_scale = nsgtk_get_scale_for_gui(gw->top_level);
+ struct browser_window *bw = gui_window_get_browser_window(g->top_level);
+ float old_scale = nsgtk_get_scale_for_gui(g->top_level);
browser_window_set_scale(bw, old_scale - 0.05, true);
return TRUE;
}
-MENUHANDLER(full_screen)
+MULTIHANDLER(fullscreen)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
-
- if (gw->fullscreen) {
- gtk_window_unfullscreen(gw->window);
+ if (g->fullscreen) {
+ gtk_window_unfullscreen(g->window);
} else {
- gtk_window_fullscreen(gw->window);
+ gtk_window_fullscreen(g->window);
}
- gw->fullscreen = !gw->fullscreen;
+ g->fullscreen = !g->fullscreen;
return TRUE;
}
-MENUHANDLER(view_source)
+MULTIHANDLER(viewsource)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
- nsgtk_source_dialog_init(gw->window,
- nsgtk_get_browser_for_gui(gw->top_level));
+ nsgtk_source_dialog_init(g->window,
+ gui_window_get_browser_window(g->top_level));
return TRUE;
}
-MENUHANDLER(menu_bar)
+MENUHANDLER(menubar)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
-
+ GtkWidget *w;
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
- gtk_widget_show(GTK_WIDGET(gw->menu_bar));
+ /* need to synchronise menus as gtk grumbles when one menu
+ * is attached to both headers */
+ w = GTK_WIDGET(g->rclick_view_menu->
+ toolbars_submenu->menubar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))
+ == FALSE)
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ TRUE);
+ w = GTK_WIDGET(g->view_menu->
+ toolbars_submenu->menubar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))
+ == FALSE)
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ TRUE);
- gtk_widget_show_all(GTK_WIDGET(gw->popup_menu));
-
- GList *widgets = glade_xml_get_widget_prefix(gw->popup_xml,
+ gtk_widget_show(GTK_WIDGET(g->menu_bar));
+
+ gtk_widget_show_all(GTK_WIDGET(g->popup_menu));
+
+ GList *widgets = glade_xml_get_widget_prefix(g->popup_xml,
"menupopup");
for (; widgets != NULL; widgets = widgets->next)
gtk_widget_hide(GTK_WIDGET(widgets->data));
+
} else {
- gtk_widget_hide(GTK_WIDGET(gw->menu_bar));
+ w = GTK_WIDGET(g->rclick_view_menu->
+ toolbars_submenu->menubar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)))
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ FALSE);
+ w = GTK_WIDGET(g->view_menu->
+ toolbars_submenu->menubar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)))
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ FALSE);
- gtk_widget_hide_all(GTK_WIDGET(gw->popup_menu));
- gtk_widget_show(GTK_WIDGET(gw->popup_menu));
+ gtk_widget_hide(GTK_WIDGET(g->menu_bar));
- GList *widgets = glade_xml_get_widget_prefix(gw->popup_xml,
+ GList *widgets = glade_xml_get_widget_prefix(g->popup_xml,
"menupopup");
for (; widgets != NULL; widgets = widgets->next)
gtk_widget_show_all(GTK_WIDGET(widgets->data));
+
}
-
return TRUE;
}
-MENUHANDLER(tool_bar)
+MENUHANDLER(toolbar)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
-
+ GtkWidget *w;
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
+
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
- gtk_widget_show(GTK_WIDGET(gw->tool_bar));
+ w = GTK_WIDGET(g->rclick_view_menu->
+ toolbars_submenu->toolbar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))
+ == FALSE)
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ TRUE);
+ w = GTK_WIDGET(g->view_menu->
+ toolbars_submenu->toolbar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))
+ == FALSE)
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ TRUE);
+ gtk_widget_show(GTK_WIDGET(g->tool_bar));
} else {
- gtk_widget_hide(GTK_WIDGET(gw->tool_bar));
+ w = GTK_WIDGET(g->rclick_view_menu->
+ toolbars_submenu->toolbar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)))
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ FALSE);
+ w = GTK_WIDGET(g->view_menu->
+ toolbars_submenu->toolbar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)))
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ FALSE);
+ gtk_widget_hide(GTK_WIDGET(g->tool_bar));
}
return TRUE;
}
-MENUHANDLER(status_bar)
+MENUHANDLER(statusbar)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
-
+ GtkWidget *w;
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
+
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
- gtk_widget_show(GTK_WIDGET(gw->status_bar));
+ w = GTK_WIDGET(g->rclick_view_menu->
+ toolbars_submenu->statusbar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))
+ == FALSE)
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ TRUE);
+ w = GTK_WIDGET(g->view_menu->
+ toolbars_submenu->statusbar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))
+ == FALSE)
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ TRUE);
+ gtk_widget_show(GTK_WIDGET(g->status_bar));
} else {
- gtk_widget_hide(GTK_WIDGET(gw->status_bar));
+ w = GTK_WIDGET(g->rclick_view_menu->
+ toolbars_submenu->statusbar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)))
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ FALSE);
+ w = GTK_WIDGET(g->view_menu->
+ toolbars_submenu->statusbar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)))
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ FALSE);
+ gtk_widget_hide(GTK_WIDGET(g->status_bar));
}
return TRUE;
}
-MENUHANDLER(downloads)
+MULTIHANDLER(downloads)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
- nsgtk_download_show(gw->window);
+ nsgtk_download_show(g->window);
return TRUE;
}
-MENUHANDLER(save_window_size)
+MULTIHANDLER(savewindowsize)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
-
- option_toolbar_status_width = gtk_paned_get_position(gw->status_pane);
- gtk_window_get_position(gw->window, &option_window_x,
+ option_toolbar_status_width = gtk_paned_get_position(g->status_pane);
+ gtk_window_get_position(g->window, &option_window_x,
&option_window_y);
- gtk_window_get_size(gw->window, &option_window_width,
+ gtk_window_get_size(g->window, &option_window_width,
&option_window_height);
@@ -979,19 +1074,18 @@
return TRUE;
}
-MENUHANDLER(toggle_debug_rendering)
+MULTIHANDLER(toggledebugging)
{
html_redraw_debug = !html_redraw_debug;
nsgtk_reflow_all_windows();
return TRUE;
}
-MENUHANDLER(save_box_tree)
+MULTIHANDLER(saveboxtree)
{
GtkWidget *save_dialog;
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
- save_dialog = gtk_file_chooser_dialog_new("Save File", gw->window,
+ save_dialog = gtk_file_chooser_dialog_new("Save File", g->window,
GTK_FILE_CHOOSER_ACTION_SAVE,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
@@ -1016,7 +1110,7 @@
"Unable to open file for writing.");
} else {
struct browser_window *bw;
- bw = nsgtk_get_browser_window(gw->top_level);
+ bw = gui_window_get_browser_window(g->top_level);
if (bw->current_content &&
bw->current_content->type ==
@@ -1037,12 +1131,11 @@
return TRUE;
}
-MENUHANDLER(save_dom_tree)
+MULTIHANDLER(savedomtree)
{
GtkWidget *save_dialog;
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
- save_dialog = gtk_file_chooser_dialog_new("Save File", gw->window,
+ save_dialog = gtk_file_chooser_dialog_new("Save File", g->window,
GTK_FILE_CHOOSER_ACTION_SAVE,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
@@ -1066,7 +1159,7 @@
"Unable to open file for writing.");
} else {
struct browser_window *bw;
- bw = nsgtk_get_browser_window(gw->top_level);
+ bw = gui_window_get_browser_window(g->top_level);
if (bw->current_content &&
bw->current_content->type ==
@@ -1088,37 +1181,94 @@
}
-MENUHANDLER(stop)
+MULTIHANDLER(stop)
{
- return nsgtk_window_stop_button_clicked(GTK_WIDGET(widget), g);
+ struct browser_window *bw = gui_window_get_browser_window(g->top_level);
+
+ browser_window_stop(bw);
+
+ return TRUE;
}
-MENUHANDLER(reload)
+MULTIHANDLER(reload)
{
- return nsgtk_window_reload_button_clicked(GTK_WIDGET(widget), g);
+ struct browser_window *bw = gui_window_get_browser_window(g->top_level);
+
+ /* clear potential search effects */
+ search_reset();
+
+ browser_window_reload(bw, true);
+
+ return TRUE;
}
-MENUHANDLER(back)
+MULTIHANDLER(back)
{
- return nsgtk_window_back_button_clicked(GTK_WIDGET(widget), g);
+ struct browser_window *bw = gui_window_get_browser_window(g->top_level);
+
+ if (!history_back_available(bw->history))
+ return TRUE;
+
+ /* clear potential search effects */
+ search_reset();
+
+ history_back(bw, bw->history);
+ nsgtk_window_update_back_forward(g);
+
+ return TRUE;
}
-MENUHANDLER(forward)
+MULTIHANDLER(forward)
{
- return nsgtk_window_forward_button_clicked(GTK_WIDGET(widget), g);
+ struct browser_window *bw = gui_window_get_browser_window(g->top_level);
+
+ if (!history_forward_available(bw->history))
+ return TRUE;
+
+ /* clear potential search effects */
+ search_reset();
+
+ history_forward(bw, bw->history);
+ nsgtk_window_update_back_forward(g);
+
+ return TRUE;
}
-MENUHANDLER(home)
+MULTIHANDLER(home)
{
- return nsgtk_window_home_button_clicked(GTK_WIDGET(widget), g);
+ static const char *addr = NETSURF_HOMEPAGE;
+ struct browser_window *bw = gui_window_get_browser_window(g->top_level);
+
+ if (option_homepage_url != NULL && option_homepage_url[0] != '\0')
+ addr = option_homepage_url;
+
+ browser_window_go(bw, addr, 0, true);
+
+ return TRUE;
}
-MENUHANDLER(local_history)
+MULTIHANDLER(localhistory)
{
- return nsgtk_window_history_button_clicked(GTK_WIDGET(widget), g);
+/* TODO: add resize handling */
+ int x,y;
+ /* if entries of the same url but different frag_ids have been added
+ * the history needs redrawing (what throbber code normally does)
+ */
+ nsgtk_window_update_back_forward(g);
+ gtk_window_get_position(g->window, &x, &y);
+ gtk_window_set_default_size(g->history_window->window, 600, 200);
+ gtk_window_set_transient_for(g->history_window->window, g->window);
+ gtk_window_set_opacity(g->history_window->window, 0.9);
+ gtk_window_set_decorated(g->history_window->window, FALSE);
+ gtk_widget_show(GTK_WIDGET(g->history_window->window));
+ gtk_window_move(g->history_window->window, x + g->historybase, y +
+ g->toolbarbase);
+ gdk_window_raise(GTK_WIDGET(g->history_window->window)->window);
+
+ return TRUE;
}
-MENUHANDLER(global_history)
+MULTIHANDLER(globalhistory)
{
gtk_widget_show(GTK_WIDGET(wndHistory));
gdk_window_raise(GTK_WIDGET(wndHistory)->window);
@@ -1126,49 +1276,89 @@
return TRUE;
}
-MENUHANDLER(next_tab)
+MULTIHANDLER(addbookmarks)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
+ return TRUE;
+}
- gtk_notebook_next_page(gw->notebook);
-
+MULTIHANDLER(showbookmarks)
+{
return TRUE;
}
-MENUHANDLER(prev_tab)
+MULTIHANDLER(openlocation)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
+ gtk_widget_grab_focus(GTK_WIDGET(g->url_bar));
+ return TRUE;
+}
- gtk_notebook_prev_page(gw->notebook);
+MULTIHANDLER(nexttab)
+{
+ gtk_notebook_next_page(g->notebook);
return TRUE;
}
-MENUHANDLER(close_tab)
+MULTIHANDLER(prevtab)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
+ gtk_notebook_prev_page(g->notebook);
- nsgtk_tab_close_current(gw->notebook);
+ return TRUE;
+}
+MULTIHANDLER(closetab)
+{
+ nsgtk_tab_close_current(g->notebook);
+
return TRUE;
}
-MENUHANDLER(about)
+MULTIHANDLER(contents)
{
- struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
- nsgtk_about_dialog_init(gw->window,
- nsgtk_get_browser_for_gui(gw->top_level),
+ return TRUE;
+}
+
+MULTIHANDLER(guide)
+{
+ return TRUE;
+}
+
+MULTIHANDLER(info)
+{
+ return TRUE;
+}
+
+MULTIHANDLER(about)
+{
+ nsgtk_about_dialog_init(g->window,
+ gui_window_get_browser_window(g->top_level),
netsurf_version);
return TRUE;
}
+BUTTONHANDLER(history)
+{
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
+ return nsgtk_on_localhistory_activate(g);
+}
+
+gboolean nsgtk_filter_directory(const GtkFileFilterInfo *info,
+ gpointer data)
+{
+ DIR *d = opendir(info->filename);
+ if (d == NULL)
+ return FALSE;
+ closedir(d);
+ return TRUE;
+}
+
/* signal handler functions for the local history window */
gboolean nsgtk_history_expose_event(GtkWidget *widget,
GdkEventExpose *event, gpointer g)
{
struct gtk_history_window *hw = (struct gtk_history_window *)g;
struct browser_window *bw =
- nsgtk_get_browser_for_gui(hw->g->top_level);
+ gui_window_get_browser_window(hw->g->top_level);
current_widget = widget;
current_drawable = widget->window;
@@ -1188,12 +1378,16 @@
return FALSE;
}
+#undef MULTIHANDLER
+#undef CHECKHANDLER
+#undef BUTTONHANDLER
+
gboolean nsgtk_history_button_press_event(GtkWidget *widget,
GdkEventButton *event, gpointer g)
{
struct gtk_history_window *hw = (struct gtk_history_window *)g;
struct browser_window *bw =
- nsgtk_get_browser_for_gui(hw->g->top_level);
+ gui_window_get_browser_window(hw->g->top_level);
LOG(("X=%g, Y=%g", event->x, event->y));
@@ -1207,7 +1401,9 @@
nsgtk_scaffolding *nsgtk_new_scaffolding(struct gui_window *toplevel)
{
- struct gtk_scaffolding *g = malloc(sizeof(*g));
+ struct gtk_scaffolding *g = malloc(sizeof(*g));
+ char *searchname;
+ int i;
LOG(("Constructing a scaffold of %p for gui_window %p", g, toplevel));
@@ -1218,29 +1414,78 @@
/* load the window template from the glade xml file, and extract
* widget references from it for later use.
*/
- g->xml = glade_xml_new(glade_file_location, "wndBrowser", NULL);
+ g->xml = glade_xml_new(glade_netsurf_file_location,
+ "wndBrowser", NULL);
glade_xml_signal_autoconnect(g->xml);
g->window = GTK_WINDOW(GET_WIDGET("wndBrowser"));
g->notebook = GTK_NOTEBOOK(GET_WIDGET("notebook"));
- g->url_bar = GTK_ENTRY(GET_WIDGET("URLBar"));
g->menu_bar = GTK_MENU_BAR(GET_WIDGET("menubar"));
g->status_bar = GTK_STATUSBAR(GET_WIDGET("statusbar"));
- g->edit_menu = GTK_MENU_ITEM(GET_WIDGET("menuitem_edit"));
- g->tabs_menu = GTK_MENU_ITEM(GET_WIDGET("menuitem_tabs"));
g->tool_bar = GTK_TOOLBAR(GET_WIDGET("toolbar"));
- g->back_button = GTK_TOOL_BUTTON(GET_WIDGET("toolBack"));
- g->forward_button = GTK_TOOL_BUTTON(GET_WIDGET("toolForward"));
- g->stop_button = GTK_TOOL_BUTTON(GET_WIDGET("toolStop"));
- g->reload_button = GTK_TOOL_BUTTON(GET_WIDGET("toolReload"));
- g->back_menu = GTK_MENU_ITEM(GET_WIDGET("back"));
- g->history_button = GTK_TOOL_BUTTON(GET_WIDGET("toolHistory"));
- g->forward_menu = GTK_MENU_ITEM(GET_WIDGET("forward"));
- g->stop_menu = GTK_MENU_ITEM(GET_WIDGET("stop"));
- g->reload_menu = GTK_MENU_ITEM(GET_WIDGET("reload"));
- g->throbber = GTK_IMAGE(GET_WIDGET("throbber"));
+ g->search = malloc(sizeof(struct gtk_search));
+ if (g->search == NULL) {
+ warn_user("NoMemory", 0);
+ return NULL;
+ }
+
+ g->search->bar = GTK_TOOLBAR(GET_WIDGET("searchbar"));
+ g->search->entry = GTK_ENTRY(GET_WIDGET("searchEntry"));
+
+ g->search->buttons[0] = GTK_TOOL_BUTTON(GET_WIDGET("searchBackButton"));
+ g->search->buttons[1] = GTK_TOOL_BUTTON(GET_WIDGET(
+ "searchForwardButton"));
+ g->search->buttons[2] = GTK_TOOL_BUTTON(GET_WIDGET(
+ "closeSearchButton"));
+ g->search->checkAll = GTK_CHECK_BUTTON(GET_WIDGET("checkAllSearch"));
+ g->search->caseSens = GTK_CHECK_BUTTON(GET_WIDGET("caseSensButton"));
+
+ GtkAccelGroup *group = gtk_accel_group_new();
+ gtk_window_add_accel_group(g->window, group);
+
+
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ g->buttons[i] = malloc(sizeof(struct nsgtk_button_connect));
+ if (g->buttons[i] == NULL) {
+ warn_user("NoMemory", 0);
+ return NULL;
+ }
+ g->buttons[i]->button = NULL;
+ g->buttons[i]->location = -1;
+ g->buttons[i]->sensitivity = true;
+ g->buttons[i]->main = NULL;
+ g->buttons[i]->rclick = NULL;
+ g->buttons[i]->popup = NULL;
+ g->buttons[i]->mhandler = NULL;
+ g->buttons[i]->bhandler = NULL;
+ g->buttons[i]->dataplus = NULL;
+ g->buttons[i]->dataminus = NULL;
+ }
+ /* here custom toolbutton adding code */
+ g->offset = 0;
+ g->toolbarmem = 0;
+ g->toolbarbase = 0;
+ g->historybase = 0;
+ nsgtk_toolbar_customization_load(g);
+ nsgtk_toolbar_set_physical(g);
+#define MAKE_MENUS(q)\
+ g->q##_menu = nsgtk_menu_##q##_menu(group);\
+ g->rclick_##q##_menu = nsgtk_menu_##q##_menu(group);\
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(GET_WIDGET("menuitem_" #q)),\
+ GTK_WIDGET(g->q##_menu->q##_menu));\
+ gtk_menu_set_accel_group(g->q##_menu->q##_menu, group)
+ MAKE_MENUS(file);
+ MAKE_MENUS(edit);
+ MAKE_MENUS(view);
+ MAKE_MENUS(nav);
+ MAKE_MENUS(tabs);
+ MAKE_MENUS(help);
+#undef MAKE_MENUS
+ g->edit_menu_item = GTK_MENU_ITEM(GET_WIDGET("menuitem_edit"));
+ g->tabs_menu_item = GTK_MENU_ITEM(GET_WIDGET("menuitem_tabs"));
+
g->preferences_dialog = NULL;
-
+
css_screen_dpi = gdk_screen_get_resolution(
gtk_widget_get_screen(GTK_WIDGET(g->window)));
LOG(("Set CSS DPI to %f", css_screen_dpi));
@@ -1309,26 +1554,19 @@
break;
}
+ gtk_toolbar_set_show_arrow(g->tool_bar, TRUE);
+ gtk_widget_show_all(GTK_WIDGET(g->tool_bar));
nsgtk_tab_init(GTK_WIDGET(g->notebook));
- /* set the URL entry box to expand, as we can't do this from within
- * glade because of the way it emulates toolbars.
- */
- gtk_tool_item_set_expand(GTK_TOOL_ITEM(GET_WIDGET("toolURLBar")),
- TRUE);
-
- /* disable toolbar buttons that make no sense initially. */
- gtk_widget_set_sensitive(GTK_WIDGET(g->back_button), FALSE);
- gtk_widget_set_sensitive(GTK_WIDGET(g->forward_button), FALSE);
- gtk_widget_set_sensitive(GTK_WIDGET(g->stop_button), FALSE);
-
+ gtk_widget_set_size_request(GTK_WIDGET(
+ g->buttons[HISTORY_BUTTON]->button), 20, -1);
+
/* create the local history window to be associated with this browser */
g->history_window = malloc(sizeof(struct gtk_history_window));
g->history_window->g = g;
g->history_window->window =
GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
gtk_window_set_transient_for(g->history_window->window, g->window);
- gtk_window_set_default_size(g->history_window->window, 400, 400);
gtk_window_set_title(g->history_window->window, "NetSurf History");
gtk_window_set_type_hint(g->history_window->window,
GDK_WINDOW_TYPE_HINT_UTILITY);
@@ -1354,7 +1592,6 @@
/* set up URL bar completion */
g->url_bar_completion = gtk_entry_completion_new();
- gtk_entry_set_completion(g->url_bar, g->url_bar_completion);
gtk_entry_completion_set_match_func(g->url_bar_completion,
nsgtk_completion_match, NULL, NULL);
gtk_entry_completion_set_model(g->url_bar_completion,
@@ -1368,8 +1605,8 @@
NULL);
/* set up the throbber. */
- gtk_image_set_from_pixbuf(g->throbber, nsgtk_throbber->framedata[0]);
g->throb_frame = 0;
+
#define CONNECT(obj, sig, callback, ptr) \
g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr))
@@ -1377,8 +1614,8 @@
/* connect history window signals to their handlers */
CONNECT(g->history_window->drawing_area, "expose_event",
nsgtk_history_expose_event, g->history_window);
-// CONNECT(g->history_window->drawing_area, "motion_notify_event",
-// nsgtk_history_motion_notify_event, g->history_window);
+ /*CONNECT(g->history_window->drawing_area, "motion_notify_event",
+ nsgtk_history_motion_notify_event, g->history_window);*/
CONNECT(g->history_window->drawing_area, "button_press_event",
nsgtk_history_button_press_event, g->history_window);
CONNECT(g->history_window->window, "delete_event",
@@ -1391,35 +1628,61 @@
/* connect signals to handlers. */
CONNECT(g->window, "delete-event", nsgtk_window_delete_event, g);
- CONNECT(g->window, "destroy", nsgtk_window_destroy_event, g);
- /* toolbar, URL bar, and menu bar signal handlers */
- CONNECT(g->edit_menu, "show", nsgtk_window_edit_menu_clicked, g);
- CONNECT(g->edit_menu, "hide", nsgtk_window_edit_menu_hidden, g);
- CONNECT(g->back_button, "clicked", nsgtk_window_back_button_clicked,
- g);
- CONNECT(g->forward_button, "clicked",
- nsgtk_window_forward_button_clicked, g);
- CONNECT(g->history_button, "clicked",
- nsgtk_window_history_button_clicked, g);
- CONNECT(g->stop_button, "clicked", nsgtk_window_stop_button_clicked,
- g);
- CONNECT(g->reload_button, "clicked",
- nsgtk_window_reload_button_clicked, g);
- CONNECT(GET_WIDGET("toolHome"), "clicked",
- nsgtk_window_home_button_clicked, g);
- CONNECT(g->url_bar, "activate", nsgtk_window_url_activate_event, g);
- CONNECT(g->url_bar, "changed", nsgtk_window_url_changed, g);
+ /* toolbar URL bar menu bar search bar signal handlers */
+ CONNECT(g->edit_menu_item, "show", nsgtk_window_edit_menu_clicked, g);
+ CONNECT(g->edit_menu_item, "hide", nsgtk_window_edit_menu_hidden, g);
+ CONNECT(g->search->buttons[1], "clicked",
+ nsgtk_search_forward_button_clicked, g);
+ CONNECT(g->search->buttons[0], "clicked",
+ nsgtk_search_back_button_clicked, g);
+ CONNECT(g->search->entry, "changed", nsgtk_search_entry_changed, g);
+ CONNECT(g->search->entry, "activate", nsgtk_search_entry_activate, g);
+ CONNECT(g->search->entry, "key-press-event", nsgtk_search_entry_key, g);
+ CONNECT(g->search->buttons[2], "clicked",
+ nsgtk_search_close_button_clicked, g);
+
+ CONNECT(g->tool_bar, "popup-context-menu",
+ nsgtk_window_tool_bar_clicked, g);
+ g->popup_xml = glade_xml_new(glade_netsurf_file_location, "menuPopup", NULL);
/* set up the menu signal handlers */
- nsgtk_attach_menu_handlers(g->xml, g);
+ nsgtk_scaffolding_toolbar_init(g);
+ nsgtk_toolbar_connect_all(g);
+ nsgtk_attach_menu_handlers(g);
+
+ /* prepare to set the web search ico */
+
+ /* init web search prefs from file */
+ search_web_provider_details(option_search_provider);
+
+ /* potentially retrieve ico */
+ if (search_ico == NULL)
+ search_ico = search_web_retrieve_ico(false);
+
+ /* set entry */
+ searchname = search_web_provider_name();
+ gtk_entry_set_text(g->webSearchEntry, searchname);
+ free(searchname);
+#define POPUP_ATTACH(q) gtk_menu_item_set_submenu( \
+ GTK_MENU_ITEM(glade_xml_get_widget(g->popup_xml,\
+ "menupopup_" #q)), GTK_WIDGET(g->rclick_##q##_menu->q##_menu));\
+
+ POPUP_ATTACH(file);
+ POPUP_ATTACH(edit);
+ POPUP_ATTACH(view);
+ POPUP_ATTACH(nav);
+ POPUP_ATTACH(tabs);
+ POPUP_ATTACH(help);
+#undef POPUP_ATTACH
+ nsgtk_scaffolding_initial_sensitivity(g);
+
g->being_destroyed = 0;
g->fullscreen = false;
/* create the popup version of the menu */
- g->popup_xml = glade_xml_new(glade_file_location, "menuPopup", NULL);
g->popup_menu = GTK_MENU(glade_xml_get_widget(g->popup_xml,
"menuPopup"));
@@ -1428,54 +1691,50 @@
* gtk_menu_shell_append (GTK_MENU_SHELL(g->popup_menu),
* GTK_WIDGET(glade_xml_get_widget(g->xml, "back"))); */
CONNECT(g->popup_menu, "hide", nsgtk_window_popup_menu_hidden, g);
- CONNECT(glade_xml_get_widget(g->popup_xml, "popupBack"), "activate",
- nsgtk_window_back_button_clicked, g);
- CONNECT(glade_xml_get_widget(g->popup_xml, "popupForward"),"activate",
- nsgtk_window_forward_button_clicked, g);
- CONNECT(glade_xml_get_widget(g->popup_xml, "popupReload"), "activate",
- nsgtk_window_reload_button_clicked, g);
+
CONNECT(glade_xml_get_widget(g->popup_xml, "save_link_popup"),
- "activate", nsgtk_on_save_link_activate, g);
+ "activate", nsgtk_on_savelink_activate, g);
CONNECT(glade_xml_get_widget(g->popup_xml,
- "open_link_in_focused_tab_popup"),
- "activate",
- nsgtk_on_open_link_in_focused_tab_activate, g);
+ "open_link_in_focused_tab_popup"), "activate",
+ nsgtk_on_linkfocused_activate, g);
CONNECT(glade_xml_get_widget(g->popup_xml,
- "open_link_in_background_tab_popup"),
- "activate",
- nsgtk_on_open_link_in_background_tab_activate, g);
+ "open_link_in_background_tab_popup"), "activate",
+ nsgtk_on_linkbackground_activate, g);
CONNECT(glade_xml_get_widget(g->popup_xml, "cut_popup"), "activate",
nsgtk_on_cut_activate, g);
CONNECT(glade_xml_get_widget(g->popup_xml, "copy_popup"), "activate",
nsgtk_on_copy_activate, g);
- CONNECT(glade_xml_get_widget(g->popup_xml, "paste_popup"),"activate",
- nsgtk_on_paste_activate, g);
-
-#define POPUP_ATTACH(x, y) gtk_menu_item_set_submenu( \
- GTK_MENU_ITEM(glade_xml_get_widget(g->popup_xml, x)),\
- GTK_WIDGET(glade_xml_get_widget(g->xml, y)))
-
- POPUP_ATTACH("menupopup_file", "menumain_file");
- POPUP_ATTACH("menupopup_edit", "menumain_edit");
- POPUP_ATTACH("menupopup_view", "menumain_view");
- POPUP_ATTACH("menupopup_navigate", "menumain_navigate");
- POPUP_ATTACH("menupopup_help", "menumain_help");
-
-#undef POPUP_ATTACH
+ CONNECT(glade_xml_get_widget(g->popup_xml, "paste_popup"), "activate",
+ nsgtk_on_paste_activate, g);
+ CONNECT(glade_xml_get_widget(g->popup_xml, "customize_popup"),
+ "activate", nsgtk_on_customize_activate, g);
/* hides redundant popup menu items */
GList *widgets = glade_xml_get_widget_prefix(g->popup_xml,
"menupopup");
for (; widgets != NULL; widgets = widgets->next)
gtk_widget_hide(GTK_WIDGET(widgets->data));
+ gtk_widget_hide(glade_xml_get_widget(g->popup_xml, "customize_popup"));
- /* disable PDF-requiring menu items */
-#ifndef WITH_PDF_EXPORT
- gtk_widget_set_sensitive(GET_WIDGET("export_pdf"), FALSE);
-#endif
+ /* attach to the list */
+ if (scaf_list)
+ scaf_list->prev = g;
+ g->next = scaf_list;
+ g->prev = NULL;
+ scaf_list = g;
+
+ /* call functions that need access from the list */
+ nsgtk_theme_init();
+ nsgtk_theme_implement(g);
+ /* set web search ico */
+ if (search_ico != NULL)
+ gui_window_set_search_ico();
+
/* finally, show the window. */
gtk_widget_show(GTK_WIDGET(g->window));
+ LOG(("creation complete"));
+
return g;
}
@@ -1521,10 +1780,9 @@
void gui_window_start_throbber(struct gui_window* _g)
{
struct gtk_scaffolding *g = nsgtk_get_scaffold(_g);
- gtk_widget_set_sensitive(GTK_WIDGET(g->stop_button), TRUE);
- gtk_widget_set_sensitive(GTK_WIDGET(g->reload_button), FALSE);
- gtk_widget_set_sensitive(GTK_WIDGET(g->stop_menu), TRUE);
- gtk_widget_set_sensitive(GTK_WIDGET(g->reload_menu), FALSE);
+ g->buttons[STOP_BUTTON]->sensitivity = true;
+ g->buttons[RELOAD_BUTTON]->sensitivity = false;
+ nsgtk_scaffolding_set_sensitivity(g);
nsgtk_window_update_back_forward(g);
@@ -1534,11 +1792,8 @@
void gui_window_stop_throbber(struct gui_window* _g)
{
struct gtk_scaffolding *g = nsgtk_get_scaffold(_g);
- gtk_widget_set_sensitive(GTK_WIDGET(g->stop_button), FALSE);
- gtk_widget_set_sensitive(GTK_WIDGET(g->reload_button), TRUE);
- gtk_widget_set_sensitive(GTK_WIDGET(g->stop_menu), FALSE);
- gtk_widget_set_sensitive(GTK_WIDGET(g->reload_menu), TRUE);
-
+ g->buttons[STOP_BUTTON]->sensitivity = false;
+ g->buttons[RELOAD_BUTTON]->sensitivity = true;
nsgtk_window_update_back_forward(g);
schedule_remove(nsgtk_throb, g);
@@ -1546,76 +1801,402 @@
gtk_image_set_from_pixbuf(g->throbber, nsgtk_throbber->framedata[0]);
}
-gboolean nsgtk_scaffolding_is_busy(struct gtk_scaffolding *scaffold)
+/**
+ * set favicon
+ */
+void gui_window_set_icon(struct gui_window *_g, struct content *icon)
{
+ struct gtk_scaffolding *g = nsgtk_get_scaffold(_g);
+ GtkImage *iconImage = NULL;
+ GtkContainer *container;
+ container = GTK_CONTAINER(gtk_bin_get_child(GTK_BIN(
+ g->buttons[URL_BAR_ITEM]->button)));
+ if (g->icoFav != NULL) {
+ g_object_ref(g->url_bar);
+ gtk_container_remove(container, GTK_WIDGET(g->icoFav));
+ gtk_container_remove(container, GTK_WIDGET(g->url_bar));
+ }
+ if ((icon != NULL) && (icon->type == CONTENT_ICO)) {
+ nsico_set_bitmap_from_size(icon, 20, 20);
+ }
+ if ((icon != NULL) && (icon->bitmap != NULL)) {
+ GdkPixbuf *pb = gtk_bitmap_get_primary(icon->bitmap);
+ if ((pb != NULL) && (gdk_pixbuf_get_width(pb) > 0) &&
+ (gdk_pixbuf_get_height(pb) > 0)) {
+ pb = gdk_pixbuf_scale_simple(pb, 20, 20,
+ GDK_INTERP_HYPER);
+ iconImage = GTK_IMAGE(
+ gtk_image_new_from_pixbuf(pb));
+ } else {
+ iconImage = NULL;
+ }
+ }
+ if (iconImage == NULL)
+ iconImage = GTK_IMAGE(gtk_image_new_from_file(g_strconcat(
+ res_dir_location, "netsurf-16x16.xpm", NULL)));
+ gtk_box_pack_start(GTK_BOX(container), GTK_WIDGET(iconImage), FALSE,
+ FALSE, 0);
+ g->icoFav = iconImage;
+ gtk_box_pack_end(GTK_BOX(container), GTK_WIDGET(g->url_bar), TRUE, TRUE,
+ 0);
+ g_object_unref(g->url_bar);
+ gtk_widget_show_all(GTK_WIDGET(g->buttons[URL_BAR_ITEM]->button));
+}
+
+void gui_window_set_search_ico()
+{
+ GdkPixbuf *pbico;
+ GtkImage *searchico = 0;
+ GtkContainer *container;
+ nsgtk_scaffolding *current;
+
+ if ((search_ico != NULL) && (search_ico->type == CONTENT_ICO)) {
+ nsico_set_bitmap_from_size(search_ico, 20, 20);
+ }
+ if ((search_ico != NULL) && (search_ico->bitmap != NULL)) {
+ pbico = gtk_bitmap_get_primary(search_ico->bitmap);
+ if ((pbico != NULL) && (gdk_pixbuf_get_width(pbico) > 0) &&
+ (gdk_pixbuf_get_height(pbico) > 0)) {
+ pbico = gdk_pixbuf_scale_simple(pbico, 20, 20,
+ GDK_INTERP_HYPER);
+ current = scaf_list;
+ while (current) {
+ container = GTK_CONTAINER(gtk_bin_get_child(
+ GTK_BIN(current->
+ buttons[WEBSEARCH_ITEM]->
+ button)));
+ gtk_container_remove(container,
+ GTK_WIDGET(
+ current->webSearchIco));
+ g_object_ref(current->webSearchEntry);
+ gtk_container_remove(container,
+ GTK_WIDGET(
+ current->webSearchEntry));
+ current = current->next;
+ }
+ searchico = GTK_IMAGE(
+ gtk_image_new_from_pixbuf(pbico));
+ } else {
+ searchico = NULL;
+ }
+ }
+ /* add ico to toolbar */
+ if (searchico != NULL) {
+ current = scaf_list;
+ while (current) {
+ container = GTK_CONTAINER(gtk_bin_get_child(
+ GTK_BIN(current->
+ buttons[WEBSEARCH_ITEM]->button)));
+ gtk_box_pack_start(GTK_BOX(container),
+ GTK_WIDGET(searchico), FALSE, FALSE, 0);
+ gtk_box_pack_end(GTK_BOX(container),
+ GTK_WIDGET(current->webSearchEntry), TRUE,
+ TRUE, 0);
+ g_object_unref(current->webSearchEntry);
+ current->webSearchIco = searchico;
+ gtk_widget_show(GTK_WIDGET(searchico));
+ searchico = GTK_IMAGE(gtk_image_new_from_pixbuf(
+ pbico));
+ current = current->next;
+ }
+ }
+
+}
+
+bool nsgtk_scaffolding_is_busy(nsgtk_scaffolding *g)
+{
/* We are considered "busy" if the stop button is sensitive */
- return GTK_WIDGET_SENSITIVE((GTK_WIDGET(scaffold->stop_button)));
+ return g->buttons[STOP_BUTTON]->sensitivity;
}
-GtkWindow* nsgtk_scaffolding_get_window (struct gui_window *g)
+GtkWindow* nsgtk_scaffolding_window(nsgtk_scaffolding *g)
{
- return g->scaffold->window;
+ return g->window;
}
-GtkNotebook* nsgtk_scaffolding_get_notebook (struct gui_window *g)
+GtkNotebook* nsgtk_scaffolding_notebook(nsgtk_scaffolding *g)
{
- return g->scaffold->notebook;
+ return g->notebook;
}
+GtkEntry *nsgtk_scaffolding_urlbar(nsgtk_scaffolding *g)
+{
+ return g->url_bar;
+}
+
+GtkEntry *nsgtk_scaffolding_websearch(nsgtk_scaffolding *g)
+{
+ return g->webSearchEntry;
+}
+
+
+GtkToolbar *nsgtk_scaffolding_toolbar(nsgtk_scaffolding *g)
+{
+ return g->tool_bar;
+}
+
+struct nsgtk_button_connect *nsgtk_scaffolding_button(nsgtk_scaffolding *g,
+ int i)
+{
+ return g->buttons[i];
+}
+
+struct gtk_search *nsgtk_scaffolding_search(nsgtk_scaffolding *g)
+{
+ return g->search;
+}
+
+GtkMenuBar *nsgtk_scaffolding_menu_bar(nsgtk_scaffolding *g)
+{
+ return g->menu_bar;
+}
+
+struct gtk_history_window *nsgtk_scaffolding_history_window(nsgtk_scaffolding
+ *g)
+{
+ return g->history_window;
+}
+
+nsgtk_scaffolding *nsgtk_scaffolding_iterate(nsgtk_scaffolding *g)
+{
+ return g->next;
+}
+
+void nsgtk_scaffolding_reset_offset(nsgtk_scaffolding *g)
+{
+ g->offset = 0;
+}
+
+void nsgtk_scaffolding_update_url_bar_ref(nsgtk_scaffolding *g)
+{
+ GList *list;
+ GtkBox *box;
+ GtkBoxChild *child;
+ box = GTK_BOX(gtk_bin_get_child(GTK_BIN(nsgtk_scaffolding_button(
+ g, URL_BAR_ITEM)->button)));
+ list = box->children;
+ while (list) {
+ child = (GtkBoxChild *)(list->data);
+ if (GTK_IS_IMAGE(child->widget))
+ g->icoFav = GTK_IMAGE(child->widget);
+ if (GTK_IS_ENTRY(child->widget))
+ g->url_bar = GTK_ENTRY(child->widget);
+ list = list->next;
+ }
+
+ gtk_entry_set_completion(g->url_bar, g->url_bar_completion);
+}
+void nsgtk_scaffolding_update_throbber_ref(nsgtk_scaffolding *g)
+{
+ g->throbber = GTK_IMAGE(gtk_bin_get_child(GTK_BIN(
+ g->buttons[THROBBER_ITEM]->button)));
+}
+
+void nsgtk_scaffolding_update_websearch_ref(nsgtk_scaffolding *g)
+{
+ GList *list;
+ GtkBox *box;
+ GtkBoxChild *child;
+ box = GTK_BOX(gtk_bin_get_child(GTK_BIN(
+ g->buttons[WEBSEARCH_ITEM]->button)));
+ list = box->children;
+ while (list) {
+ child = (GtkBoxChild *)(list->data);
+ if (GTK_IS_IMAGE(child->widget))
+ g->webSearchIco = GTK_IMAGE(child->widget);
+ if (GTK_IS_ENTRY(child->widget))
+ g->webSearchEntry = GTK_ENTRY(child->widget);
+ list = list->next;
+ }
+}
+
+void nsgtk_scaffolding_set_websearch(nsgtk_scaffolding *g, const char *content)
+{
+ gtk_entry_set_text(g->webSearchEntry, content);
+}
+
+void nsgtk_scaffolding_toggle_search_bar_visibility(nsgtk_scaffolding *g)
+{
+ gboolean vis;
+ g_object_get(G_OBJECT(g->search->bar), "visible", &vis, NULL);
+ if (vis) {
+ search_reset();
+ gtk_widget_hide(GTK_WIDGET(g->search->bar));
+ } else {
+ gtk_widget_show(GTK_WIDGET(g->search->bar));
+ gtk_widget_grab_focus(GTK_WIDGET(g->search->entry));
+ }
+}
+
+
+struct gui_window *nsgtk_scaffolding_top_level(nsgtk_scaffolding *g)
+{
+ return g->top_level;
+}
+
void nsgtk_scaffolding_set_top_level (struct gui_window *gw)
{
- gw->scaffold->top_level = gw;
+ nsgtk_get_scaffold(gw)->top_level = gw;
/* Synchronise the history (will also update the URL bar) */
- nsgtk_window_update_back_forward(gw->scaffold);
+ nsgtk_window_update_back_forward(nsgtk_get_scaffold(gw));
+ /* clear effects of potential searches */
+ search_reset();
- /* Ensure the window's title bar is updated */
- if (gw->bw != NULL && gw->bw->current_content != NULL) {
- if (gw->bw->current_content->title != NULL) {
+ /* Ensure the window's title bar as well as favicon are updated */
+ if (gui_window_get_browser_window(gw) != NULL &&
+ gui_window_get_browser_window(gw)->current_content
+ != NULL) {
+ if (gui_window_get_browser_window(gw)->current_content->title
+ != NULL) {
gui_window_set_title(gw,
- gw->bw->current_content->title);
+ gui_window_get_browser_window(gw)->
+ current_content->title);
} else {
- gui_window_set_title(gw, gw->bw->current_content->url);
+ gui_window_set_title(gw,
+ gui_window_get_browser_window(gw)->
+ current_content->url);
}
+ if (gui_window_get_browser_window(gw)->current_content->type
+ == CONTENT_HTML)
+ gui_window_set_icon(gw,
+ gui_window_get_browser_window(gw)->
+ current_content->data.html.favicon);
}
}
+void nsgtk_scaffolding_set_sensitivity(struct gtk_scaffolding *g)
+{
+#define SENSITIVITY(q)\
+ i = q##_BUTTON;\
+ if (g->buttons[i]->main != NULL)\
+ gtk_widget_set_sensitive(GTK_WIDGET(\
+ g->buttons[i]->main),\
+ g->buttons[i]->sensitivity);\
+ if (g->buttons[i]->rclick != NULL)\
+ gtk_widget_set_sensitive(GTK_WIDGET(\
+ g->buttons[i]->rclick),\
+ g->buttons[i]->sensitivity);\
+ if ((g->buttons[i]->location != -1) && \
+ (g->buttons[i]->button != NULL))\
+ gtk_widget_set_sensitive(GTK_WIDGET(\
+ g->buttons[i]->button),\
+ g->buttons[i]->sensitivity);\
+ if (g->buttons[i]->popup != NULL)\
+ gtk_widget_set_sensitive(GTK_WIDGET(\
+ g->buttons[i]->popup),\
+ g->buttons[i]->sensitivity);
+
+ int i;
+ SENSITIVITY(STOP)
+ SENSITIVITY(RELOAD)
+ SENSITIVITY(CUT)
+ SENSITIVITY(COPY)
+ SENSITIVITY(PASTE)
+ SENSITIVITY(BACK)
+ SENSITIVITY(FORWARD)
+ SENSITIVITY(NEXTTAB)
+ SENSITIVITY(PREVTAB)
+ SENSITIVITY(CLOSETAB)
+#undef SENSITIVITY
+}
+
+void nsgtk_scaffolding_initial_sensitivity(struct gtk_scaffolding *g)
+{
+ for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ if (g->buttons[i]->main != NULL)
+ gtk_widget_set_sensitive(GTK_WIDGET(
+ g->buttons[i]->main),
+ g->buttons[i]->sensitivity);
+ if (g->buttons[i]->rclick != NULL)
+ gtk_widget_set_sensitive(GTK_WIDGET(
+ g->buttons[i]->rclick),
+ g->buttons[i]->sensitivity);
+ if ((g->buttons[i]->location != -1) &&
+ (g->buttons[i]->button != NULL))
+ gtk_widget_set_sensitive(GTK_WIDGET(
+ g->buttons[i]->button),
+ g->buttons[i]->sensitivity);
+ if (g->buttons[i]->popup != NULL)
+ gtk_widget_set_sensitive(GTK_WIDGET(
+ g->buttons[i]->popup),
+ g->buttons[i]->sensitivity);
+ }
+ gtk_widget_set_sensitive(GTK_WIDGET(g->view_menu->images_menuitem),
+ FALSE);
+}
+
void nsgtk_scaffolding_popup_menu(struct gtk_scaffolding *g,
gdouble x, gdouble y)
{
guint available_menu_options = 0;
GtkWidget *widget = NULL;
+
available_menu_options |=
nsgtk_scaffolding_update_link_operations_sensitivity(g,
- g->popup_xml, x, y, TRUE);
+ g->popup_xml, x, y, true);
available_menu_options |=
nsgtk_scaffolding_update_edit_actions_sensitivity(g,
- g->popup_xml, TRUE);
+ g->popup_xml, true);
/* Hide the separator as well */
if (!available_menu_options) {
- widget = glade_xml_get_widget(g->popup_xml, "separator1");
+ widget = glade_xml_get_widget(g->popup_xml, "sep2");
gtk_widget_hide(widget);
}
+ /* hide customize */
+ gtk_widget_hide(glade_xml_get_widget(g->popup_xml, "customize_popup"));
+
gtk_menu_popup(g->popup_menu, NULL, NULL, NULL, NULL, 0,
gtk_get_current_event_time());
}
+/**
+ * reallocate width for history button, reallocate buttons right of history;
+ * memorise base of history button / toolbar
+ */
+void nsgtk_scaffolding_toolbar_size_allocate(GtkWidget *widget, GtkAllocation
+ *alloc, gpointer data)
+{
+ struct gtk_scaffolding *g = (struct gtk_scaffolding *)data;
+ if ((g->toolbarmem == alloc->x) ||
+ (g->buttons[nsgtk_toolbar_get_id_from_widget(
+ widget, g)]->location <
+ g->buttons[HISTORY_BUTTON]->location))
+ /* no reallocation after first adjustment, no reallocation for buttons
+ * left of history button */
+ return;
+ if (widget == GTK_WIDGET(g->buttons[HISTORY_BUTTON]->button)) {
+ if (alloc->width == 20)
+ return;
+ g->toolbarbase = alloc->y + alloc->height;
+ g->historybase = alloc->x;
+ if (g->offset == 0)
+ g->offset = alloc->width - 20;
+ alloc->width = 20;
+ } else {
+ alloc->x -= g->offset;
+ g->toolbarmem = alloc->x;
+ }
+ gtk_widget_size_allocate(widget, alloc);
+}
+
static guint nsgtk_scaffolding_update_link_operations_sensitivity(
struct gtk_scaffolding *g, GladeXML *xml, gdouble x, gdouble y,
gboolean hide)
{
gboolean is_sensitive;
- GtkWidget *widget1, *widget2, *widget3;
+ GtkWidget *widget[3];
+ int i;
- widget1 = glade_xml_get_widget_prefix(xml, "save_link")->data;
- widget2 = glade_xml_get_widget_prefix(xml,
+ widget[0] = glade_xml_get_widget_prefix(xml, "save_link")->data;
+ widget[1] = glade_xml_get_widget_prefix(xml,
"open_link_in_focused_tab")->data;
- widget3 = glade_xml_get_widget_prefix(xml,
+ widget[2] = glade_xml_get_widget_prefix(xml,
"open_link_in_background_tab")->data;
- struct browser_window *bw = nsgtk_get_browser_for_gui(g->top_level);
+ struct browser_window *bw = gui_window_get_browser_window(g->top_level);
current_menu_link_box = NULL;
if (bw->current_content && bw->current_content->type == CONTENT_HTML) {
@@ -1624,107 +2205,228 @@
}
is_sensitive = (current_menu_link_box != NULL) ? TRUE : FALSE;
- gtk_widget_set_sensitive(widget1, is_sensitive);
- gtk_widget_set_sensitive(widget2, is_sensitive);
- gtk_widget_set_sensitive(widget3, is_sensitive);
+ for (i = 0; i < 3; i++) {
+ gtk_widget_set_sensitive(widget[i], is_sensitive);
- if (hide == TRUE && current_menu_link_box == NULL) {
- gtk_widget_hide(widget1);
- gtk_widget_hide(widget2);
- gtk_widget_hide(widget3);
+ if (hide == true && current_menu_link_box == NULL) {
+ gtk_widget_hide(widget[i]);
+ }
}
return is_sensitive;
}
static guint nsgtk_scaffolding_update_edit_actions_sensitivity(
- struct gtk_scaffolding *g, GladeXML *xml, gboolean hide)
+ struct gtk_scaffolding *g, GladeXML *xml, bool hide)
{
GtkWidget *widget = gtk_window_get_focus(g->window);
- gboolean can_copy, can_cut, can_paste;
gboolean has_selection;
- if (GTK_IS_EDITABLE (widget)) {
- has_selection = gtk_editable_get_selection_bounds
- (GTK_EDITABLE (widget), NULL, NULL);
+ if (GTK_IS_EDITABLE(widget)) {
+ has_selection = gtk_editable_get_selection_bounds(
+ GTK_EDITABLE (widget), NULL, NULL);
- can_copy = has_selection;
- can_cut = has_selection;
- can_paste = TRUE;
+ g->buttons[COPY_BUTTON]->sensitivity = has_selection;
+ g->buttons[CUT_BUTTON]->sensitivity = has_selection;
+ g->buttons[PASTE_BUTTON]->sensitivity = true;
} else {
struct browser_window *bw =
- nsgtk_get_browser_for_gui(g->top_level);
+ gui_window_get_browser_window(g->top_level);
has_selection = bw->sel->defined;
- can_copy = has_selection;
- can_cut = (has_selection && bw->caret_callback != 0);
- can_paste = (bw->paste_callback != 0);
+ g->buttons[COPY_BUTTON]->sensitivity = has_selection;
+ g->buttons[CUT_BUTTON]->sensitivity = (has_selection &&
+ bw->caret_callback != 0);
+ g->buttons[PASTE_BUTTON]->sensitivity =
+ (bw->paste_callback != 0);
}
widget = glade_xml_get_widget_prefix(xml, "copy")->data;
- gtk_widget_set_sensitive (widget, can_copy);
- if (hide && !can_copy)
+ if (hide && !(g->buttons[COPY_BUTTON]->sensitivity))
gtk_widget_hide(widget);
widget = glade_xml_get_widget_prefix(xml, "cut")->data;
- gtk_widget_set_sensitive (widget, can_cut);
- if (hide && !can_cut)
+ if (hide && !(g->buttons[CUT_BUTTON]->sensitivity))
gtk_widget_hide(widget);
widget = glade_xml_get_widget_prefix(xml, "paste")->data;
- gtk_widget_set_sensitive (widget, can_paste);
- if (hide && !can_paste)
+ if (hide && !(g->buttons[PASTE_BUTTON]->sensitivity))
gtk_widget_hide(widget);
-
- return (can_paste | can_cut | can_copy);
+ nsgtk_scaffolding_set_sensitivity(g);
+ return ((g->buttons[COPY_BUTTON]->sensitivity) |
+ (g->buttons[CUT_BUTTON]->sensitivity) |
+ (g->buttons[PASTE_BUTTON]->sensitivity));
}
static void nsgtk_scaffolding_enable_link_operations_sensitivity(
struct gtk_scaffolding *g, GladeXML *xml)
{
- GtkWidget *widget1;
- GtkWidget *widget2;
- GtkWidget *widget3;
+ int i;
+ GtkWidget *widget[3];
- widget1 = glade_xml_get_widget_prefix(xml, "save_link")->data;
- widget2 = glade_xml_get_widget_prefix(xml,
+ widget[0] = glade_xml_get_widget_prefix(xml, "save_link")->data;
+ widget[1] = glade_xml_get_widget_prefix(xml,
"open_link_in_focused_tab")->data;
- widget3 = glade_xml_get_widget_prefix(xml,
+ widget[2] = glade_xml_get_widget_prefix(xml,
"open_link_in_background_tab")->data;
- gtk_widget_set_sensitive(widget1, TRUE);
- gtk_widget_show(widget1);
- gtk_widget_set_sensitive(widget2, TRUE);
- gtk_widget_show(widget2);
- gtk_widget_set_sensitive(widget3, TRUE);
- gtk_widget_show(widget3);
-
-
+ for (i = 0; i < 3; i++) {
+ gtk_widget_set_sensitive(widget[i], TRUE);
+ gtk_widget_show(widget[i]);
+ }
}
static void nsgtk_scaffolding_enable_edit_actions_sensitivity(
struct gtk_scaffolding *g, GladeXML *xml)
{
- GtkWidget *widget1;
- GtkWidget *widget2;
- GtkWidget *widget3;
- GtkWidget *widget4;
- GtkWidget *widget5;
+ int i;
+ GtkWidget *widget[5];
- widget1 = glade_xml_get_widget(xml, "separator");
- widget2 = glade_xml_get_widget(xml, "separator1");
- widget3 = glade_xml_get_widget_prefix(xml, "copy")->data;
- widget4 = glade_xml_get_widget_prefix(xml, "cut")->data;
- widget5 = glade_xml_get_widget_prefix(xml, "paste")->data;
-
- gtk_widget_set_sensitive(widget3, TRUE);
- gtk_widget_set_sensitive(widget4, TRUE);
- gtk_widget_set_sensitive(widget5, TRUE);
-
- gtk_widget_show(widget1);
- gtk_widget_show(widget2);
- gtk_widget_show(widget3);
- gtk_widget_show(widget4);
- gtk_widget_show(widget5);
+ widget[0] = glade_xml_get_widget(xml, "sep");
+ widget[1] = glade_xml_get_widget(xml, "sep2");
+ widget[2] = glade_xml_get_widget_prefix(xml, "copy")->data;
+ widget[3] = glade_xml_get_widget_prefix(xml, "cut")->data;
+ widget[4] = glade_xml_get_widget_prefix(xml, "paste")->data;
+ g->buttons[PASTE_BUTTON]->sensitivity = true;
+ g->buttons[COPY_BUTTON]->sensitivity = true;
+ g->buttons[CUT_BUTTON]->sensitivity = true;
+ nsgtk_scaffolding_set_sensitivity(g);
+
+ for (i = 0; i < 5; i++)
+ gtk_widget_show(widget[i]);
+
}
+
+/**
+ * init the array g->buttons[]
+ */
+void nsgtk_scaffolding_toolbar_init(struct gtk_scaffolding *g)
+{
+#define ITEM_MAIN(p, q, r)\
+ g->buttons[p##_BUTTON]->main =\
+ g->q##_menu->r##_menuitem;\
+ g->buttons[p##_BUTTON]->rclick =\
+ g->rclick_##q##_menu->r##_menuitem;\
+ g->buttons[p##_BUTTON]->mhandler =\
+ nsgtk_on_##r##_activate_menu;\
+ g->buttons[p##_BUTTON]->bhandler =\
+ nsgtk_on_##r##_activate_button;\
+ g->buttons[p##_BUTTON]->dataplus =\
+ nsgtk_toolbar_##r##_button_data;\
+ g->buttons[p##_BUTTON]->dataminus =\
+ nsgtk_toolbar_##r##_toolbar_button_data
+#define ITEM_SUB(p, q, r, s)\
+ g->buttons[p##_BUTTON]->main =\
+ g->q##_menu->\
+ r##_submenu->s##_menuitem;\
+ g->buttons[p##_BUTTON]->rclick =\
+ g->rclick_##q##_menu->\
+ r##_submenu->s##_menuitem;\
+ g->buttons[p##_BUTTON]->mhandler =\
+ nsgtk_on_##s##_activate_menu;\
+ g->buttons[p##_BUTTON]->bhandler =\
+ nsgtk_on_##s##_activate_button;\
+ g->buttons[p##_BUTTON]->dataplus =\
+ nsgtk_toolbar_##s##_button_data;\
+ g->buttons[p##_BUTTON]->dataminus =\
+ nsgtk_toolbar_##s##_toolbar_button_data
+#define ITEM_BUTTON(p, q)\
+ g->buttons[p##_BUTTON]->bhandler =\
+ nsgtk_on_##q##_activate;\
+ g->buttons[p##_BUTTON]->dataplus =\
+ nsgtk_toolbar_##q##_button_data;\
+ g->buttons[p##_BUTTON]->dataminus =\
+ nsgtk_toolbar_##q##_toolbar_button_data
+#define ITEM_POP(p, q)\
+ g->buttons[p##_BUTTON]->popup = GTK_IMAGE_MENU_ITEM(\
+ glade_xml_get_widget(g->popup_xml, #q "_popup"))
+#define SENSITIVITY(q)\
+ g->buttons[q##_BUTTON]->sensitivity = false
+#define ITEM_ITEM(p, q)\
+ g->buttons[p##_ITEM]->dataplus =\
+ nsgtk_toolbar_##q##_button_data;\
+ g->buttons[p##_ITEM]->dataminus =\
+ nsgtk_toolbar_##q##_toolbar_button_data
+ ITEM_ITEM(WEBSEARCH, websearch);
+ ITEM_ITEM(THROBBER, throbber);
+ ITEM_MAIN(NEWWINDOW, file, newwindow);
+ ITEM_MAIN(NEWTAB, file, newtab);
+ ITEM_MAIN(OPENFILE, file, openfile);
+ ITEM_MAIN(PRINT, file, print);
+ ITEM_MAIN(CLOSEWINDOW, file, closewindow);
+ ITEM_MAIN(SAVEPAGE, file, savepage);
+ ITEM_MAIN(PRINTPREVIEW, file, printpreview);
+ ITEM_MAIN(PRINT, file, print);
+ ITEM_MAIN(QUIT, file, quit);
+ ITEM_MAIN(CUT, edit, cut);
+ ITEM_MAIN(COPY, edit, copy);
+ ITEM_MAIN(PASTE, edit, paste);
+ ITEM_MAIN(DELETE, edit, delete);
+ ITEM_MAIN(SELECTALL, edit, selectall);
+ ITEM_MAIN(FIND, edit, find);
+ ITEM_MAIN(PREFERENCES, edit, preferences);
+ ITEM_MAIN(STOP, view, stop);
+ ITEM_POP(STOP, stop);
+ ITEM_MAIN(RELOAD, view, reload);
+ ITEM_POP(RELOAD, reload);
+ ITEM_MAIN(FULLSCREEN, view, fullscreen);
+ ITEM_MAIN(VIEWSOURCE, view, viewsource);
+ ITEM_MAIN(DOWNLOADS, view, downloads);
+ ITEM_MAIN(SAVEWINDOWSIZE, view, savewindowsize);
+ ITEM_MAIN(BACK, nav, back);
+ ITEM_POP(BACK, back);
+ ITEM_MAIN(FORWARD, nav, forward);
+ ITEM_POP(FORWARD, forward);
+ ITEM_MAIN(HOME, nav, home);
+ ITEM_MAIN(LOCALHISTORY, nav, localhistory);
+ ITEM_MAIN(GLOBALHISTORY, nav, globalhistory);
+ ITEM_MAIN(ADDBOOKMARKS, nav, addbookmarks);
+ ITEM_MAIN(SHOWBOOKMARKS, nav, showbookmarks);
+ ITEM_MAIN(OPENLOCATION, nav, openlocation);
+ ITEM_MAIN(NEXTTAB, tabs, nexttab);
+ ITEM_MAIN(PREVTAB, tabs, prevtab);
+ ITEM_MAIN(CLOSETAB, tabs, closetab);
+ ITEM_MAIN(CONTENTS, help, contents);
+ ITEM_MAIN(INFO, help, info);
+ ITEM_MAIN(GUIDE, help, guide);
+ ITEM_MAIN(ABOUT, help, about);
+ ITEM_SUB(PLAINTEXT, file, export, plaintext);
+ ITEM_SUB(PDF, file, export, pdf);
+ ITEM_SUB(DRAWFILE, file, export, drawfile);
+ ITEM_SUB(POSTSCRIPT, file, export, postscript);
+ ITEM_SUB(ZOOMPLUS, view, scaleview, zoomplus);
+ ITEM_SUB(ZOOMMINUS, view, scaleview, zoomminus);
+ ITEM_SUB(ZOOMNORMAL, view, scaleview, zoomnormal);
+ ITEM_SUB(TOGGLEDEBUGGING, view, debugging, toggledebugging);
+ ITEM_SUB(SAVEBOXTREE, view, debugging, saveboxtree);
+ ITEM_SUB(SAVEDOMTREE, view, debugging, savedomtree);
+ ITEM_BUTTON(HISTORY, history);
+ /* disable items that make no sense initially, as well as
+ * as-yet-unimplemented items */
+ SENSITIVITY(BACK);
+ SENSITIVITY(FORWARD);
+ SENSITIVITY(STOP);
+ SENSITIVITY(PRINTPREVIEW);
+ SENSITIVITY(DELETE);
+ SENSITIVITY(CONTENTS);
+ SENSITIVITY(DRAWFILE);
+ SENSITIVITY(POSTSCRIPT);
+ SENSITIVITY(ADDBOOKMARKS);
+ SENSITIVITY(SHOWBOOKMARKS);
+ SENSITIVITY(NEXTTAB);
+ SENSITIVITY(PREVTAB);
+ SENSITIVITY(CLOSETAB);
+ SENSITIVITY(GUIDE);
+ SENSITIVITY(INFO);
+#ifndef WITH_PDF_EXPORT
+ SENSITIVITY(PDF);
+#endif
+
+#undef ITEM_MAIN
+#undef ITEM_SUB
+#undef ITEM_BUTTON
+#undef ITEM_POP
+#undef SENSITIVITY
+
+}
Index: gtk/gtk_scaffolding.h
===================================================================
--- gtk/gtk_scaffolding.h (revision 8438)
+++ gtk/gtk_scaffolding.h (working copy)
@@ -21,63 +21,218 @@
#include <gtk/gtk.h>
#include <glade/glade.h>
+#include <glib.h>
#include "desktop/gui.h"
#include "desktop/plotters.h"
+#include "gtk/gtk_menu.h"
typedef struct gtk_scaffolding nsgtk_scaffolding;
-struct gtk_scaffolding {
+typedef enum {
+ BACK_BUTTON = 0,
+ HISTORY_BUTTON,
+ FORWARD_BUTTON,
+ STOP_BUTTON,
+ RELOAD_BUTTON,
+ HOME_BUTTON,
+ URL_BAR_ITEM,
+ THROBBER_ITEM,
+ WEBSEARCH_ITEM,
+ NEWWINDOW_BUTTON,
+ NEWTAB_BUTTON,
+ OPENFILE_BUTTON,
+ CLOSETAB_BUTTON,
+ CLOSEWINDOW_BUTTON,
+ SAVEPAGE_BUTTON,
+ PDF_BUTTON,
+ PLAINTEXT_BUTTON,
+ DRAWFILE_BUTTON,
+ POSTSCRIPT_BUTTON,
+ PRINTPREVIEW_BUTTON,
+ PRINT_BUTTON,
+ QUIT_BUTTON,
+ CUT_BUTTON,
+ COPY_BUTTON,
+ PASTE_BUTTON,
+ DELETE_BUTTON,
+ SELECTALL_BUTTON,
+ FIND_BUTTON,
+ PREFERENCES_BUTTON,
+ ZOOMPLUS_BUTTON,
+ ZOOMMINUS_BUTTON,
+ ZOOMNORMAL_BUTTON,
+ FULLSCREEN_BUTTON,
+ VIEWSOURCE_BUTTON,
+ DOWNLOADS_BUTTON,
+ SAVEWINDOWSIZE_BUTTON,
+ TOGGLEDEBUGGING_BUTTON,
+ SAVEBOXTREE_BUTTON,
+ SAVEDOMTREE_BUTTON,
+ LOCALHISTORY_BUTTON,
+ GLOBALHISTORY_BUTTON,
+ ADDBOOKMARKS_BUTTON,
+ SHOWBOOKMARKS_BUTTON,
+ OPENLOCATION_BUTTON,
+ NEXTTAB_BUTTON,
+ PREVTAB_BUTTON,
+ CONTENTS_BUTTON,
+ GUIDE_BUTTON,
+ INFO_BUTTON,
+ ABOUT_BUTTON,
+ PLACEHOLDER_BUTTON /* size indicator; array maximum indices */
+} nsgtk_toolbar_button; /* PLACEHOLDER_BUTTON - 1 */
+
+struct gtk_history_window {
+ struct gtk_scaffolding *g;
GtkWindow *window;
- GtkNotebook *notebook;
- GtkEntry *url_bar;
- GtkEntryCompletion *url_bar_completion;
- GtkStatusbar *status_bar;
- GtkMenuItem *edit_menu;
- GtkMenuItem *tabs_menu;
- GtkToolbar *tool_bar;
- GtkToolButton *back_button;
- GtkToolButton *history_button;
- GtkToolButton *forward_button;
- GtkToolButton *stop_button;
- GtkToolButton *reload_button;
- GtkMenuBar *menu_bar;
- GtkMenuItem *back_menu;
- GtkMenuItem *forward_menu;
- GtkMenuItem *stop_menu;
- GtkMenuItem *reload_menu;
- GtkImage *throbber;
- GtkPaned *status_pane;
+ GtkScrolledWindow *scrolled;
+ GtkDrawingArea *drawing_area;
+};
- GladeXML *xml;
+struct gtk_search {
+ GtkToolbar *bar;
+ GtkEntry *entry;
+ GtkToolButton *buttons[3]; /* back, forward, */
+ GtkCheckButton *checkAll; /* close */
+ GtkCheckButton *caseSens;
+};
- GladeXML *popup_xml;
- GtkMenu *popup_menu;
+struct nsgtk_button_connect {
+ GtkToolItem *button;
+ int location; /* in toolbar */
+ bool sensitivity;
+ GtkImageMenuItem *main;
+ GtkImageMenuItem *rclick;
+ GtkImageMenuItem *popup;
+ void *mhandler; /* menu item clicked */
+ void *bhandler; /* button clicked */
+ void *dataplus; /* customization -> toolbar */
+ void *dataminus; /* customization -> store */
+};
- struct gtk_history_window *history_window;
- GtkDialog *preferences_dialog;
+extern nsgtk_scaffolding *scaf_list;
- int throb_frame;
- struct gui_window *top_level;
- int being_destroyed;
+nsgtk_scaffolding *nsgtk_new_scaffolding(struct gui_window *toplevel);
- bool fullscreen;
-};
+bool nsgtk_scaffolding_is_busy(nsgtk_scaffolding *g);
-GtkWindow *nsgtk_get_window_for_scaffold(struct gtk_scaffolding *g);
+GtkWindow *nsgtk_scaffolding_window(nsgtk_scaffolding *g);
+GtkNotebook *nsgtk_scaffolding_notebook(nsgtk_scaffolding *g);
+GtkEntry *nsgtk_scaffolding_urlbar(nsgtk_scaffolding *g);
+GtkEntry *nsgtk_scaffolding_websearch(nsgtk_scaffolding *g);
+GtkToolbar *nsgtk_scaffolding_toolbar(nsgtk_scaffolding *g);
+struct nsgtk_button_connect *nsgtk_scaffolding_button(nsgtk_scaffolding *g,
+ int i);
+struct gtk_search *nsgtk_scaffolding_search(nsgtk_scaffolding *g);
+GtkMenuBar *nsgtk_scaffolding_menu_bar(nsgtk_scaffolding *g);
+struct gtk_history_window *nsgtk_scaffolding_history_window(nsgtk_scaffolding
+ *g);
+struct gui_window *nsgtk_scaffolding_top_level(nsgtk_scaffolding *g);
+void nsgtk_scaffolding_reset_offset(nsgtk_scaffolding *g);
+nsgtk_scaffolding *nsgtk_scaffolding_iterate(nsgtk_scaffolding *g);
+void nsgtk_scaffolding_toolbar_init(struct gtk_scaffolding *g);
+void nsgtk_scaffolding_update_url_bar_ref(nsgtk_scaffolding *g);
+void nsgtk_scaffolding_update_throbber_ref(nsgtk_scaffolding *g);
+void nsgtk_scaffolding_update_websearch_ref(nsgtk_scaffolding *g);
+void nsgtk_scaffolding_set_websearch(nsgtk_scaffolding *g, const char
+ *content);
+void nsgtk_scaffolding_toggle_search_bar_visibility(nsgtk_scaffolding *g);
+void nsgtk_scaffolding_set_top_level(struct gui_window *g);
-nsgtk_scaffolding *nsgtk_new_scaffolding(struct gui_window *toplevel);
+void nsgtk_scaffolding_destroy(nsgtk_scaffolding *g);
-gboolean nsgtk_scaffolding_is_busy(nsgtk_scaffolding *scaffold);
+void nsgtk_scaffolding_set_sensitivity(struct gtk_scaffolding *g);
+void nsgtk_scaffolding_initial_sensitivity(struct gtk_scaffolding *g);
+void nsgtk_scaffolding_popup_menu(struct gtk_scaffolding *g, gdouble x,
+ gdouble y);
+void nsgtk_scaffolding_toolbar_size_allocate(GtkWidget *widget,
+ GtkAllocation *alloc, gpointer data);
-GtkWindow* nsgtk_scaffolding_get_window (struct gui_window *g);
+gboolean nsgtk_window_url_activate_event(GtkWidget *, gpointer);
+gboolean nsgtk_window_url_changed(GtkWidget *, GdkEventKey *, gpointer);
-GtkNotebook* nsgtk_scaffolding_get_notebook (struct gui_window *g);
+#define MULTIPROTO(q)\
+gboolean nsgtk_on_##q##_activate(struct gtk_scaffolding *);\
+gboolean nsgtk_on_##q##_activate_menu(GtkMenuItem *, gpointer);\
+gboolean nsgtk_on_##q##_activate_button(GtkButton *, gpointer)
+#define MENUPROTO(q)\
+gboolean nsgtk_on_##q##_activate(GtkMenuItem *, gpointer)
+#define BUTTONPROTO(q)\
+gboolean nsgtk_on_##q##_activate(GtkButton *, gpointer)
+/* prototypes for handlers */
+/* file menu */
+MULTIPROTO(newwindow);
+MULTIPROTO(newtab);
+MULTIPROTO(open_location);
+MULTIPROTO(openfile);
+MULTIPROTO(savepage);
+MULTIPROTO(pdf);
+MULTIPROTO(plaintext);
+MULTIPROTO(drawfile);
+MULTIPROTO(postscript);
+MULTIPROTO(printpreview);
+MULTIPROTO(print);
+MULTIPROTO(closewindow);
+MULTIPROTO(quit);
-void nsgtk_scaffolding_set_top_level (struct gui_window *gw);
+/* edit menu */
+MULTIPROTO(cut);
+MULTIPROTO(copy);
+MULTIPROTO(paste);
+MULTIPROTO(delete);
+MULTIPROTO(selectall);
+MULTIPROTO(find);
+MULTIPROTO(preferences);
-void nsgtk_scaffolding_destroy(nsgtk_scaffolding *scaffold);
+/* view menu */
+MULTIPROTO(stop);
+MULTIPROTO(reload);
+MULTIPROTO(zoomplus);
+MULTIPROTO(zoomnormal);
+MULTIPROTO(zoomminus);
+MULTIPROTO(fullscreen);
+MULTIPROTO(viewsource);
+MENUPROTO(menubar);
+MENUPROTO(toolbar);
+MENUPROTO(statusbar);
+MULTIPROTO(downloads);
+MULTIPROTO(savewindowsize);
+MULTIPROTO(toggledebugging);
+MULTIPROTO(saveboxtree);
+MULTIPROTO(savedomtree);
-void nsgtk_scaffolding_popup_menu(struct gtk_scaffolding *g, gdouble x,
- gdouble y);
+/* navigate menu */
+MULTIPROTO(back);
+MULTIPROTO(forward);
+MULTIPROTO(home);
+MULTIPROTO(localhistory);
+MULTIPROTO(globalhistory);
+MULTIPROTO(addbookmarks);
+MULTIPROTO(showbookmarks);
+MULTIPROTO(openlocation);
+/* tabs menu */
+MULTIPROTO(nexttab);
+MULTIPROTO(prevtab);
+MULTIPROTO(closetab);
+
+/* help menu */
+MULTIPROTO(contents);
+MULTIPROTO(guide);
+MULTIPROTO(info);
+MULTIPROTO(about);
+
+/* popup menu */
+MENUPROTO(customize);
+MENUPROTO(savelink);
+MENUPROTO(linkfocused);
+MENUPROTO(linkbackground);
+
+/* non-menu */
+BUTTONPROTO(history);
+
+#undef MULTIPROTO
+#undef MENUPROTO
+#undef BUTTONPROTO
+
#endif /* NETSURF_GTK_SCAFFOLDING_H */
Index: gtk/gtk_tabs.c
===================================================================
--- gtk/gtk_tabs.c (revision 8438)
+++ gtk/gtk_tabs.c (working copy)
@@ -23,12 +23,13 @@
#include "desktop/browser.h"
#include "content/content.h"
#include "desktop/options.h"
+#include "desktop/search.h"
#include "utils/utils.h"
#include "gtk/options.h"
#include "gtk/gtk_tabs.h"
#define TAB_WIDTH_N_CHARS 15
-#define GET_WIDGET(x) glade_xml_get_widget(gladeWindows, (x))
+#define GET_WIDGET(x) glade_xml_get_widget(gladeNetsurf, (x))
static GtkWidget *nsgtk_tab_label_setup(struct gui_window *window);
static void nsgtk_tab_visibility_update(GtkNotebook *notebook, GtkWidget *child,
@@ -58,15 +59,19 @@
void nsgtk_tab_add(struct gui_window *window, bool background)
{
- GtkWidget *tabs = GTK_WIDGET(nsgtk_scaffolding_get_notebook(window));
+ GtkWidget *tabs = GTK_WIDGET(nsgtk_scaffolding_notebook(
+ nsgtk_get_scaffold(window)));
GtkWidget *tabBox = nsgtk_tab_label_setup(window);
gint remember = gtk_notebook_get_current_page(GTK_NOTEBOOK(tabs));
gtk_notebook_append_page(GTK_NOTEBOOK(tabs),
- GTK_WIDGET(window->scrolledwindow), tabBox);
- /*causes gtk errors can't set a parent
+ GTK_WIDGET(nsgtk_window_get_scrolledwindow(window)),
+ tabBox);
+ /*causes gtk errors can't set a parent */
gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(tabs),
- GTK_WIDGET(window->scrolledwindow), true); */
- gtk_widget_show_all(GTK_WIDGET(window->scrolledwindow));
+ GTK_WIDGET(nsgtk_window_get_scrolledwindow(window)),
+ true);
+ gtk_widget_show_all(GTK_WIDGET(nsgtk_window_get_scrolledwindow(
+ window)));
gtk_notebook_set_current_page(GTK_NOTEBOOK(tabs),
gtk_notebook_get_n_pages(GTK_NOTEBOOK(tabs)) - 1);
if (option_new_blank) {
@@ -75,13 +80,15 @@
blankpage = g_strconcat("file:///", res_dir_location,
"blankpage", NULL); */
/* segfaults
- struct browser_window *bw = nsgtk_get_browser_for_gui(window);
+ struct browser_window *bw =
+ gui_window_get_browser_window(window);
browser_window_go(bw, blankpage, 0, true); */
/* free(blankpage); */
}
if (background)
gtk_notebook_set_current_page(GTK_NOTEBOOK(tabs), remember);
- gtk_widget_grab_focus(GTK_WIDGET(window->scaffold->url_bar));
+ gtk_widget_grab_focus(GTK_WIDGET(nsgtk_scaffolding_urlbar(
+ nsgtk_get_scaffold(window))));
}
void nsgtk_tab_visibility_update(GtkNotebook *notebook, GtkWidget *child,
@@ -97,12 +104,14 @@
void nsgtk_tab_set_title(struct gui_window *g, const char *title)
{
GtkWidget *label;
- gboolean is_top_level = (g->tab != NULL);
+ GtkWidget *tab;
+ tab = nsgtk_window_get_tab(g);
+ gboolean is_top_level = (tab != NULL);
if (is_top_level) {
- label = g_object_get_data(G_OBJECT(g->tab), "label");
+ label = g_object_get_data(G_OBJECT(tab), "label");
gtk_label_set_text(GTK_LABEL(label), title);
- gtk_widget_set_tooltip_text(g->tab, title);
+ gtk_widget_set_tooltip_text(tab, title);
}
}
@@ -147,7 +156,7 @@
g_object_set_data(G_OBJECT(hbox), "label", label);
g_object_set_data(G_OBJECT(hbox), "close-button", button);
- window->tab = hbox;
+ nsgtk_window_set_tab(window, hbox);
gtk_widget_show_all(hbox);
return hbox;
@@ -183,6 +192,7 @@
GtkWidget *window = gtk_notebook_get_nth_page(notebook, page_num);
struct gui_window *gw = g_object_get_data(G_OBJECT(window),
"gui_window");
+ search_reset();
if (gw)
nsgtk_scaffolding_set_top_level(gw);
}
Index: gtk/dialogs/gtk_options.c
===================================================================
--- gtk/dialogs/gtk_options.c (revision 8438)
+++ gtk/dialogs/gtk_options.c (working copy)
@@ -2,6 +2,7 @@
* Copyright 2006 Rob Kendrick <rjek(a)rjek.com>
* Copyright 2008 Mike Lester <element3260(a)gmail.com>
* Copyright 2009 Daniel Silverstone <dsilvers(a)netsurf-browser.org>
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin(a)dfgh.net>
*
* This file is part of NetSurf, http://www.netsurf-browser.org/
*
@@ -27,15 +28,18 @@
#include <glade/glade.h>
#include "desktop/options.h"
#include "desktop/print.h"
+#include "desktop/searchweb.h"
#include "gtk/options.h"
#include "gtk/gtk_gui.h"
#include "gtk/gtk_scaffolding.h"
+#include "gtk/gtk_theme.h"
#include "gtk/dialogs/gtk_options.h"
#include "gtk/gtk_window.h"
#include "utils/log.h"
#include "utils/utils.h"
+#include "utils/messages.h"
-GtkDialog *wndPreferences;
+GtkDialog *wndPreferences = NULL;
static GladeXML *gladeFile;
static gchar *glade_location;
static struct browser_window *current_browser;
@@ -97,6 +101,12 @@
DECLARE(fileChooserDownloads);
DECLARE(checkFocusNew);
DECLARE(checkNewBlank);
+DECLARE(checkUrlSearch);
+DECLARE(comboSearch);
+DECLARE(combotheme);
+DECLARE(buttonaddtheme);
+DECLARE(sourceButtonTab);
+static GtkWidget *sourceButtonWindow;
DECLARE(spinMarginTop);
DECLARE(spinMarginBottom);
@@ -133,7 +143,13 @@
wndPreferences = GTK_DIALOG(glade_xml_get_widget(gladeFile,
"dlgPreferences"));
gtk_window_set_transient_for (GTK_WINDOW(wndPreferences), parent);
-
+
+ FIND_WIDGET(sourceButtonTab);
+ FIND_WIDGET(sourceButtonWindow);
+ GSList *group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(
+ sourceButtonWindow));
+ gtk_radio_button_set_group(GTK_RADIO_BUTTON(sourceButtonTab), group);
+
/* set the widgets to reflect the current options */
nsgtk_options_load();
@@ -188,7 +204,13 @@
CONNECT(checkFocusNew, "toggled");
CONNECT(checkNewBlank, "toggled");
+ CONNECT(checkUrlSearch, "toggled");
+ CONNECT(comboSearch, "changed");
+ CONNECT(combotheme, "changed");
+ CONNECT(buttonaddtheme, "clicked");
+ CONNECT(sourceButtonTab, "toggled");
+
CONNECT(spinMarginTop, "value-changed");
CONNECT(spinMarginBottom, "value-changed");
CONNECT(spinMarginLeft, "value-changed");
@@ -247,11 +269,11 @@
(value)); \
} while (0)
-#define SET_FILE_CHOOSER(widgt, value) \
+#define SET_FILE_CHOOSER(widget, value) \
do { \
- (widgt) = glade_xml_get_widget(gladeFile, #widgt); \
- gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER((widgt)), \
- (value)); \
+ (widget) = glade_xml_get_widget(gladeFile, #widget); \
+ gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(\
+ (widget)), (value)); \
} while (0)
#define SET_BUTTON(widget) \
@@ -262,19 +284,18 @@
void nsgtk_options_load(void)
{
- GtkVBox *combolanguagevbox;
- gchar *languagefile;
+ GtkBox *box;
+ gchar *languagefile, *themefile;
const char *default_accept_language =
option_accept_language ? option_accept_language : "en";
int combo_row_count = 0;
int active_language = 0;
int proxytype = 0;
FILE *fp;
- char buf[20];
+ char buf[50];
/* Create combobox */
- combolanguagevbox =
- GTK_VBOX(glade_xml_get_widget(gladeFile, "combolanguagevbox"));
+ box = GTK_BOX(glade_xml_get_widget(gladeFile, "combolanguagevbox"));
comboLanguage = gtk_combo_box_new_text();
languagefile = g_strconcat(res_dir_location, "languages", NULL);
@@ -309,10 +330,35 @@
/** \todo localisation */
gtk_widget_set_tooltip_text(GTK_WIDGET(comboLanguage),
"set preferred language for web pages");
- gtk_box_pack_start(GTK_BOX(combolanguagevbox),
- comboLanguage, FALSE, FALSE, 0);
+ gtk_box_pack_start(box, comboLanguage, FALSE, FALSE, 0);
gtk_widget_show(comboLanguage);
+
+ /* populate theme combo from themelist file */
+ box = GTK_BOX(glade_xml_get_widget(gladeFile, "themehbox"));
+ combotheme = gtk_combo_box_new_text();
+ themefile = g_strconcat(res_dir_location, "themelist", NULL);
+ fp = fopen((const char *)themefile, "r");
+ g_free(themefile);
+ if (fp == NULL) {
+ LOG(("Failed opening themes file"));
+ warn_user("FileError", (const char *) themefile);
+ return;
+ }
+ while (fgets(buf, sizeof(buf), fp)) {
+ /* Ignore blank lines */
+ if (buf[0] == '\0')
+ continue;
+ /* Remove trailing \n */
+ buf[strlen(buf) - 1] = '\0';
+
+ gtk_combo_box_append_text(GTK_COMBO_BOX(combotheme), buf);
+ }
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combotheme),
+ option_current_theme);
+ gtk_box_pack_start(box, combotheme, FALSE, TRUE, 0);
+ gtk_widget_show(combotheme);
+
SET_ENTRY(entryHomePageURL,
option_homepage_url ? option_homepage_url : "");
SET_BUTTON(setCurrentPage);
@@ -380,7 +426,12 @@
SET_CHECK(checkFocusNew, option_focus_new);
SET_CHECK(checkNewBlank, option_new_blank);
+ SET_CHECK(checkUrlSearch, option_search_url_bar);
+ SET_COMBO(comboSearch, option_search_provider);
+ SET_BUTTON(buttonaddtheme);
+ SET_CHECK(sourceButtonTab, option_source_tab);
+
SET_SPIN(spinMarginTop, option_margin_top);
SET_SPIN(spinMarginBottom, option_margin_bottom);
SET_SPIN(spinMarginLeft, option_margin_left);
@@ -417,6 +468,14 @@
}
return stay_alive;
}
+
+bool nsgtk_options_combo_theme_add(const char *themename)
+{
+ if (wndPreferences == NULL)
+ return false;
+ gtk_combo_box_append_text(GTK_COMBO_BOX(combotheme), themename);
+ return true;
+}
/* Defines the callback functions for all widgets and specifies
@@ -643,42 +702,43 @@
END_HANDLER
COMBO_CHANGED(comboButtonType, option_button_type)
- struct gui_window *current = window_list;
+ nsgtk_scaffolding *current = scaf_list;
while (current) {
+ nsgtk_scaffolding_reset_offset(current);
switch(option_button_type) {
case 0:
gtk_toolbar_set_style(
- GTK_TOOLBAR(current->scaffold->tool_bar),
+ GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)),
GTK_TOOLBAR_ICONS);
gtk_toolbar_set_icon_size(
- GTK_TOOLBAR(current->scaffold->tool_bar),
+ GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)),
GTK_ICON_SIZE_SMALL_TOOLBAR);
break;
case 1:
gtk_toolbar_set_style(
- GTK_TOOLBAR(current->scaffold->tool_bar),
+ GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)),
GTK_TOOLBAR_ICONS);
gtk_toolbar_set_icon_size(
- GTK_TOOLBAR(current->scaffold->tool_bar),
+ GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)),
GTK_ICON_SIZE_LARGE_TOOLBAR);
break;
case 2:
gtk_toolbar_set_style(
- GTK_TOOLBAR(current->scaffold->tool_bar),
+ GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)),
GTK_TOOLBAR_BOTH);
gtk_toolbar_set_icon_size(
- GTK_TOOLBAR(current->scaffold->tool_bar),
+ GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)),
GTK_ICON_SIZE_LARGE_TOOLBAR);
break;
case 3:
gtk_toolbar_set_style(
- GTK_TOOLBAR(current->scaffold->tool_bar),
+ GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)),
GTK_TOOLBAR_TEXT);
default:
break;
}
- current = current->next;
+ current = nsgtk_scaffolding_iterate(current);
}
END_HANDLER
@@ -704,6 +764,100 @@
CHECK_CHANGED(checkNewBlank, option_new_blank)
END_HANDLER
+CHECK_CHANGED(checkUrlSearch, option_search_url_bar)
+END_HANDLER
+
+COMBO_CHANGED(comboSearch, option_search_provider)
+ nsgtk_scaffolding *current = scaf_list;
+ char *name;
+ /* refresh web search prefs from file */
+ search_web_provider_details(option_search_provider);
+ /* retrieve ico */
+ search_ico = search_web_retrieve_ico(false);
+ /* callback may handle changing gui */
+ if (search_ico != NULL)
+ gui_window_set_search_ico();
+ /* set entry */
+ name = search_web_provider_name();
+ while (current) {
+ nsgtk_scaffolding_set_websearch(current, name);
+ current = nsgtk_scaffolding_iterate(current);
+ }
+ free(name);
+END_HANDLER
+
+COMBO_CHANGED(combotheme, option_current_theme)
+ nsgtk_scaffolding *current = scaf_list;
+ if (option_current_theme != 0) {
+ if (current_theme_name != NULL)
+ free(current_theme_name);
+ current_theme_name = strdup(gtk_combo_box_get_active_text(
+ GTK_COMBO_BOX(combotheme)));
+ nsgtk_theme_prepare();
+ } else if (current_theme_name != NULL) {
+ free(current_theme_name);
+ current_theme_name = NULL;
+ }
+ while (current) {
+ nsgtk_theme_implement(current);
+ current = nsgtk_scaffolding_iterate(current);
+ }
+END_HANDLER
+
+BUTTON_CLICKED(buttonaddtheme)
+ char *themesfolder, *filename, *directory;
+ GtkWidget *fc = gtk_file_chooser_dialog_new(
+ messages_get("gtkAddThemeTitle"),
+ GTK_WINDOW(wndPreferences),
+ GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
+ GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
+ themesfolder = g_strconcat(res_dir_location, "themes", NULL);
+ gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(fc),
+ themesfolder);
+ gint res = gtk_dialog_run(GTK_DIALOG(fc));
+ if (res == GTK_RESPONSE_ACCEPT) {
+ filename = gtk_file_chooser_get_current_folder(
+ GTK_FILE_CHOOSER(fc));
+ if (strcmp(filename, themesfolder) != 0) {
+ directory = strrchr(filename, '/');
+ *directory = '\0';
+ if (strcmp(filename, themesfolder) != 0) {
+ warn_user(messages_get(
+ "gtkThemeFolderInstructions"),
+ 0);
+ gtk_widget_destroy(GTK_WIDGET(fc));
+ free(filename);
+ free(themesfolder);
+ return FALSE;
+ } else {
+ directory++;
+ }
+ } else {
+ free(filename);
+ filename = gtk_file_chooser_get_filename(
+ GTK_FILE_CHOOSER(fc));
+ if (strcmp(filename, themesfolder) == 0) {
+ warn_user(messages_get("gtkThemeFolderSub"),
+ 0);
+ gtk_widget_destroy(GTK_WIDGET(fc));
+ free(filename);
+ free(themesfolder);
+ return FALSE;
+ }
+ directory = strrchr(filename, '/') + 1;
+ }
+ gtk_widget_destroy(GTK_WIDGET(fc));
+ nsgtk_theme_add(directory);
+ free(filename);
+ free(themesfolder);
+ }
+
+END_HANDLER
+
+CHECK_CHANGED(sourceButtonTab, option_source_tab)
+END_HANDLER
+
SPIN_CHANGED(spinMarginTop, option_margin_top)
END_HANDLER
Index: gtk/dialogs/gtk_options.h
===================================================================
--- gtk/dialogs/gtk_options.h (revision 8438)
+++ gtk/dialogs/gtk_options.h (working copy)
@@ -1,5 +1,6 @@
/*
* Copyright 2006 Rob Kendrick <rjek(a)rjek.com>
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin(a)dfgh.net>
*
* This file is part of NetSurf, http://www.netsurf-browser.org/
*
@@ -26,5 +27,6 @@
GtkDialog* nsgtk_options_init(struct browser_window *bw, GtkWindow *parent); /** Init options and load window */
void nsgtk_options_load(void); /** Load current options into window */
void nsgtk_options_save(void); /** Save options from window */
+bool nsgtk_options_combo_theme_add(const char *themename); /** add new theme name to combo */
#endif
Index: gtk/dialogs/gtk_source.c
===================================================================
--- gtk/dialogs/gtk_source.c (revision 8438)
+++ gtk/dialogs/gtk_source.c (working copy)
@@ -28,8 +28,10 @@
#include "gtk/gtk_gui.h"
#include "gtk/gtk_print.h"
#include "gtk/gtk_selection.h"
+#include "gtk/options.h"
#include "desktop/netsurf.h"
#include "desktop/print.h"
+#include "desktop/options.h"
#include "utils/messages.h"
#include "utils/url.h"
#include "utils/utils.h"
@@ -61,6 +63,7 @@
static struct nsgtk_source_window *nsgtk_source_list = 0;
static char source_zoomlevel = 10;
+void nsgtk_source_tab_init(GtkWindow *parent, struct browser_window *bw);
static void nsgtk_attach_source_menu_handlers(GladeXML *xml, gpointer g);
static gboolean nsgtk_source_delete_event(GtkWindow *window, gpointer g);
static gboolean nsgtk_source_destroy_event(GtkWindow *window, gpointer g);
@@ -103,111 +106,148 @@
void nsgtk_source_dialog_init(GtkWindow *parent, struct browser_window *bw)
{
- if (bw->current_content->type == CONTENT_HTML) {
- glade_Location = g_strconcat(res_dir_location, "source.glade",
- NULL);
- glade_File = glade_xml_new(glade_Location, NULL, NULL);
- if (glade_File == NULL) {
- LOG(("error loading glade tree"));
- }
+ if (bw->current_content->type != CONTENT_HTML)
+ return;
+
+ if (option_source_tab) {
+ nsgtk_source_tab_init(parent, bw);
+ return;
+ }
+
+ glade_Location = g_strconcat(res_dir_location, "source.glade",
+ NULL);
+ glade_File = glade_xml_new(glade_Location, NULL, NULL);
+ if (glade_File == NULL) {
+ LOG(("error loading glade tree"));
+ }
+ g_free(glade_Location);
- char *data = NULL;
+ char *data = NULL;
- utf8_convert_ret r = utf8_from_enc(
- bw->current_content->source_data,
- bw->current_content->data.html.encoding,
- bw->current_content->source_size,
- &data);
- if (r == UTF8_CONVERT_NOMEM) {
- warn_user("NoMemory",0);
- return;
- } else if (r == UTF8_CONVERT_BADENC) {
- warn_user("EncNotRec",0);
- return;
- }
+ utf8_convert_ret r = utf8_from_enc(
+ bw->current_content->source_data,
+ bw->current_content->data.html.encoding,
+ bw->current_content->source_size,
+ &data);
+ if (r == UTF8_CONVERT_NOMEM) {
+ warn_user("NoMemory",0);
+ return;
+ } else if (r == UTF8_CONVERT_BADENC) {
+ warn_user("EncNotRec",0);
+ return;
+ }
- GtkWindow *wndSource = GTK_WINDOW(glade_xml_get_widget(
- glade_File, "wndSource"));
- GtkWidget *cutbutton = glade_xml_get_widget(
- glade_File, "source_cut");
- GtkWidget *pastebutton = glade_xml_get_widget(
- glade_File, "source_paste");
- GtkWidget *deletebutton = glade_xml_get_widget(
- glade_File, "source_delete");
- GtkWidget *printbutton = glade_xml_get_widget(
- glade_File, "source_print");
- gtk_widget_set_sensitive(cutbutton, FALSE);
- gtk_widget_set_sensitive(pastebutton, FALSE);
- gtk_widget_set_sensitive(deletebutton, FALSE);
- /* for now */
- gtk_widget_set_sensitive(printbutton, FALSE);
-
- struct nsgtk_source_window *thiswindow =
- malloc(sizeof(struct nsgtk_source_window));
- if (thiswindow == NULL) {
- free(data);
- warn_user("NoMemory", 0);
- return;
- }
+ GtkWindow *wndSource = GTK_WINDOW(glade_xml_get_widget(
+ glade_File, "wndSource"));
+ GtkWidget *cutbutton = glade_xml_get_widget(
+ glade_File, "source_cut");
+ GtkWidget *pastebutton = glade_xml_get_widget(
+ glade_File, "source_paste");
+ GtkWidget *deletebutton = glade_xml_get_widget(
+ glade_File, "source_delete");
+ GtkWidget *printbutton = glade_xml_get_widget(
+ glade_File, "source_print");
+ gtk_widget_set_sensitive(cutbutton, FALSE);
+ gtk_widget_set_sensitive(pastebutton, FALSE);
+ gtk_widget_set_sensitive(deletebutton, FALSE);
+ /* for now */
+ gtk_widget_set_sensitive(printbutton, FALSE);
+
+ struct nsgtk_source_window *thiswindow =
+ malloc(sizeof(struct nsgtk_source_window));
+ if (thiswindow == NULL) {
+ free(data);
+ warn_user("NoMemory", 0);
+ return;
+ }
- thiswindow->url = strdup(bw->current_content->url);
- if (thiswindow->url == NULL) {
- free(thiswindow);
- free(data);
- warn_user("NoMemory", 0);
- return;
- }
+ thiswindow->url = strdup(bw->current_content->url);
+ if (thiswindow->url == NULL) {
+ free(thiswindow);
+ free(data);
+ warn_user("NoMemory", 0);
+ return;
+ }
- thiswindow->data = data;
-
- thiswindow->sourcewindow = wndSource;
- thiswindow->bw = bw;
+ thiswindow->data = data;
- char *title = malloc(strlen(bw->current_content->url)
- + SLEN("Source of ") + 1);
- if (title == NULL) {
- free(thiswindow->url);
- free(thiswindow);
- free(data);
- warn_user("NoMemory", 0);
- return;
- }
- sprintf(title, "Source of %s", bw->current_content->url);
+ thiswindow->sourcewindow = wndSource;
+ thiswindow->bw = bw;
- thiswindow->next = nsgtk_source_list;
- thiswindow->prev = NULL;
- if (nsgtk_source_list != NULL)
- nsgtk_source_list->prev = thiswindow;
- nsgtk_source_list = thiswindow;
-
- nsgtk_attach_source_menu_handlers(glade_File, thiswindow);
+ char *title = malloc(strlen(bw->current_content->url)
+ + SLEN("Source of ") + 1);
+ if (title == NULL) {
+ free(thiswindow->url);
+ free(thiswindow);
+ free(data);
+ warn_user("NoMemory", 0);
+ return;
+ }
+ sprintf(title, "Source of %s", bw->current_content->url);
+
+ thiswindow->next = nsgtk_source_list;
+ thiswindow->prev = NULL;
+ if (nsgtk_source_list != NULL)
+ nsgtk_source_list->prev = thiswindow;
+ nsgtk_source_list = thiswindow;
- gtk_window_set_title(wndSource, title);
+ nsgtk_attach_source_menu_handlers(glade_File, thiswindow);
- g_signal_connect(G_OBJECT(wndSource), "destroy",
- G_CALLBACK(nsgtk_source_destroy_event),
- thiswindow);
- g_signal_connect(G_OBJECT(wndSource), "delete-event",
- G_CALLBACK(nsgtk_source_delete_event),
- thiswindow);
+ gtk_window_set_title(wndSource, title);
+
+ g_signal_connect(G_OBJECT(wndSource), "destroy",
+ G_CALLBACK(nsgtk_source_destroy_event),
+ thiswindow);
+ g_signal_connect(G_OBJECT(wndSource), "delete-event",
+ G_CALLBACK(nsgtk_source_delete_event),
+ thiswindow);
+
+ GtkTextView *sourceview = GTK_TEXT_VIEW(
+ glade_xml_get_widget(glade_File,
+ "source_view"));
+ PangoFontDescription *fontdesc =
+ pango_font_description_from_string("Monospace 8");
+
+ thiswindow->gv = sourceview;
+ gtk_widget_modify_font(GTK_WIDGET(sourceview), fontdesc);
+ GtkTextBuffer *tb = gtk_text_view_get_buffer(sourceview);
+ gtk_text_buffer_set_text(tb, thiswindow->data, -1);
- GtkTextView *sourceview = GTK_TEXT_VIEW(
- glade_xml_get_widget(glade_File,
- "source_view"));
- PangoFontDescription *fontdesc =
- pango_font_description_from_string("Monospace 8");
+ gtk_widget_show(GTK_WIDGET(wndSource));
- thiswindow->gv = sourceview;
- gtk_widget_modify_font(GTK_WIDGET(sourceview), fontdesc);
- GtkTextBuffer *tb = gtk_text_view_get_buffer(sourceview);
- gtk_text_buffer_set_text(tb, thiswindow->data, -1);
-
- gtk_widget_show(GTK_WIDGET(wndSource));
-
- free(title);
+ free(title);
+}
+void nsgtk_source_tab_init(GtkWindow *parent, struct browser_window *bw)
+{
+ char *ndata = 0;
+ utf8_convert_ret r = utf8_from_enc(
+ bw->current_content->source_data,
+ bw->current_content->data.html.encoding,
+ bw->current_content->source_size,
+ &ndata);
+ if (r == UTF8_CONVERT_NOMEM) {
+ warn_user("NoMemory",0);
+ return;
+ } else if (r == UTF8_CONVERT_BADENC) {
+ warn_user("EncNotRec",0);
+ return;
}
+ gchar *filename;
+ gint handle = g_file_open_tmp("nsgtksourceXXXXXX", &filename, NULL);
+ close (handle); /* in case it was binary mode */
+ FILE *f = fopen(filename, "w");
+ fprintf(f, "%s", ndata);
+ fclose(f);
+ filename = g_strconcat("file://", filename, NULL);
+ struct browser_window *newbw = browser_window_create(filename, bw,
+ NULL, false, true);
+ g_free(filename);
+ if (newbw->current_content)
+ newbw->current_content->title = g_strconcat("source of ",
+ bw->current_content->url , NULL);
}
+
void nsgtk_attach_source_menu_handlers(GladeXML *xml, gpointer g)
{
struct menu_events *event = source_menu_events;
Index: gtk/res/netsurf.glade
===================================================================
--- gtk/res/netsurf.glade (revision 8438)
+++ gtk/res/netsurf.glade (working copy)
@@ -12,197 +12,10 @@
<widget class="GtkMenuBar" id="menubar">
<property name="visible">True</property>
<child>
- <widget class="GtkMenuItem" id="menuitem_main">
+ <widget class="GtkMenuItem" id="menuitem_file">
<property name="visible">True</property>
<property name="label" translatable="yes">_File</property>
<property name="use_underline">True</property>
- <child>
- <widget class="GtkMenu" id="menuitem_main_menu">
- <child>
- <widget class="GtkImageMenuItem" id="new_window">
- <property name="visible">True</property>
- <property name="tooltip" translatable="yes">Opens a new browser window.</property>
- <property name="label" translatable="yes">_New Window</property>
- <property name="use_underline">True</property>
- <accelerator key="n" modifiers="GDK_CONTROL_MASK" signal="activate"/>
- <child internal-child="image">
- <widget class="GtkImage" id="image608">
- <property name="visible">True</property>
- <property name="stock">gtk-new</property>
- <property name="icon_size">1</property>
- </widget>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkImageMenuItem" id="new_tab">
- <property name="visible">True</property>
- <property name="tooltip" translatable="yes">Opens a new browser tab.</property>
- <property name="label" translatable="yes">New _Tab</property>
- <property name="use_underline">True</property>
- <accelerator key="t" modifiers="GDK_CONTROL_MASK" signal="activate"/>
- <child internal-child="image">
- <widget class="GtkImage" id="image609">
- <property name="visible">True</property>
- <property name="stock">gtk-new</property>
- <property name="icon_size">1</property>
- </widget>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkImageMenuItem" id="open_file">
- <property name="visible">True</property>
- <property name="tooltip" translatable="yes">Open a file on your computer into this browser window.</property>
- <property name="label" translatable="yes">_Open File...</property>
- <property name="use_underline">True</property>
- <accelerator key="o" modifiers="GDK_CONTROL_MASK" signal="activate"/>
- <child internal-child="image">
- <widget class="GtkImage" id="image610">
- <property name="visible">True</property>
- <property name="stock">gtk-open</property>
- <property name="icon_size">1</property>
- </widget>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkImageMenuItem" id="close_window">
- <property name="visible">True</property>
- <property name="tooltip" translatable="yes">Close this browser window.</property>
- <property name="label" translatable="yes">_Close Window</property>
- <property name="use_underline">True</property>
- <accelerator key="w" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK" signal="activate"/>
- <child internal-child="image">
- <widget class="GtkImage" id="image611">
- <property name="visible">True</property>
- <property name="stock">gtk-close</property>
- <property name="icon_size">1</property>
- </widget>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkSeparatorMenuItem" id="separatormenuitem1">
- <property name="visible">True</property>
- </widget>
- </child>
- <child>
- <widget class="GtkImageMenuItem" id="save_page">
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="tooltip" translatable="yes">Save this page to disc, optionally including images, etc.</property>
- <property name="label" translatable="yes">Save page...</property>
- <property name="use_underline">True</property>
- <child internal-child="image">
- <widget class="GtkImage" id="image612">
- <property name="visible">True</property>
- <property name="stock">gtk-save-as</property>
- <property name="icon_size">1</property>
- </widget>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="export">
- <property name="visible">True</property>
- <property name="tooltip" translatable="yes">Export the page to a different format.</property>
- <property name="label" translatable="yes">Export</property>
- <property name="use_underline">True</property>
- <child>
- <widget class="GtkMenu" id="export_menu">
- <child>
- <widget class="GtkMenuItem" id="export_plain_text">
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="tooltip" translatable="yes">Plain ASCII text, readable in text editors and views.</property>
- <property name="label" translatable="yes">Plain text...</property>
- <property name="use_underline">True</property>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="export_drawfile">
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="tooltip" translatable="yes">RISC OS Drawfile vector graphic.</property>
- <property name="label" translatable="yes">Drawfile...</property>
- <property name="use_underline">True</property>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="export_postscript">
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="tooltip" translatable="yes">PostScript for printing and converting to PDFs.</property>
- <property name="label" translatable="yes">PostScript...</property>
- <property name="use_underline">True</property>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="export_pdf">
- <property name="visible">True</property>
- <property name="tooltip" translatable="yes">Portable Document Format.</property>
- <property name="label" translatable="yes">PDF...</property>
- <property name="use_underline">True</property>
- </widget>
- </child>
- </widget>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkSeparatorMenuItem" id="separator2">
- <property name="visible">True</property>
- </widget>
- </child>
- <child>
- <widget class="GtkImageMenuItem" id="print_preview">
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="tooltip" translatable="yes">Show how a print out might look like.</property>
- <property name="label" translatable="yes">Print preview...</property>
- <property name="use_underline">True</property>
- <accelerator key="P" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK" signal="activate"/>
- <child internal-child="image">
- <widget class="GtkImage" id="image613">
- <property name="visible">True</property>
- <property name="stock">gtk-print-preview</property>
- <property name="icon_size">1</property>
- </widget>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkImageMenuItem" id="print">
- <property name="visible">True</property>
- <property name="tooltip" translatable="yes">Produce a hardcopy on your printer.</property>
- <property name="label" translatable="yes">Print...</property>
- <property name="use_underline">True</property>
- <accelerator key="P" modifiers="GDK_CONTROL_MASK" signal="activate"/>
- <child internal-child="image">
- <widget class="GtkImage" id="image614">
- <property name="visible">True</property>
- <property name="stock">gtk-print</property>
- <property name="icon_size">1</property>
- </widget>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkSeparatorMenuItem" id="separator3">
- <property name="visible">True</property>
- </widget>
- </child>
- <child>
- <widget class="GtkImageMenuItem" id="quit">
- <property name="visible">True</property>
- <property name="label">gtk-quit</property>
- <property name="use_underline">True</property>
- <property name="use_stock">True</property>
- </widget>
- </child>
- </widget>
- </child>
</widget>
</child>
<child>
@@ -210,93 +23,6 @@
<property name="visible">True</property>
<property name="label" translatable="yes">_Edit</property>
<property name="use_underline">True</property>
- <child>
- <widget class="GtkMenu" id="menuitem_edit_menu">
- <child>
- <widget class="GtkImageMenuItem" id="cut">
- <property name="visible">True</property>
- <property name="label">gtk-cut</property>
- <property name="use_underline">True</property>
- <property name="use_s