/* * Copyright (C) 2000-2003 Marco Pesenti Gritti * * This program 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; either version 2, or (at your option) * any later version. * * This program 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * $Id$ */ #include "config.h" #include "ephy-embed.h" #include "ephy-marshal.h" #include "mozilla-embed-single.h" #include "mozilla-embed.h" static void ephy_embed_base_init (gpointer g_class); GType ephy_embed_chrome_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) { static const GFlagsValue values[] = { { EPHY_EMBED_CHROME_MENUBAR, "EPHY_EMBED_CHROME_MENUBAR", "menubar" }, { EPHY_EMBED_CHROME_TOOLBAR, "EPHY_EMBED_CHROME_TOOLBAR", "toolbar" }, { EPHY_EMBED_CHROME_STATUSBAR, "EPHY_EMBED_CHROME_STATUSBAR", "statusbar" }, { EPHY_EMBED_CHROME_BOOKMARKSBAR, "EPHY_EMBED_CHROME_BOOKMARKSBAR", "bookmarksbar" }, { 0, NULL, NULL } }; type = g_flags_register_static ("EphyEmbedChrome", values); } return type; } GType ephy_embed_document_type_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) { static const GEnumValue values[] = { { EMBED_DOCUMENT_HTML, "EMBED_DOCUMENT_HTML", "html" }, { EMBED_DOCUMENT_XML, "EMBED_DOCUMENT_XML", "xml" }, { EMBED_DOCUMENT_IMAGE, "EMBED_DOCUMENT_IMAGE", "image" }, { EMBED_DOCUMENT_OTHER, "EMBED_DOCUMENT_OTHER", "other" }, { 0, NULL, NULL } }; type = g_enum_register_static ("EphyEmbedDocumentType", values); } return type; } GType ephy_embed_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) { static const GTypeInfo our_info = { sizeof (EphyEmbedIface), ephy_embed_base_init, NULL, }; type = g_type_register_static (G_TYPE_INTERFACE, "EphyEmbed", &our_info, (GTypeFlags)0); } return type; } static void ephy_embed_base_init (gpointer g_class) { static gboolean initialized = FALSE; if (!initialized) { /** * EphyEmbed::ge-new-window: * @embed: * @new_embed: a newly-generated child #EphyEmbed * @mask: @new_embed's #EphyChromeMask * * The ::ge_new_window signal is emitted when a new window has been opened by * the embed. For example, when a JavaScript popup window is opened. **/ g_signal_new ("ge_new_window", EPHY_TYPE_EMBED, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (EphyEmbedIface, new_window), NULL, NULL, ephy_marshal_VOID__POINTER_INT, G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_INT); /** * EphyEmbed::ge-popup-blocked: * @embed: * @address: The requested URL * @features: The requested features: for example, "height=400,width=200" * * The ::ge_popup_blocked signal is emitted when the viewed web page requests * a popup window (with javascript:open()) but popup windows are not allowed. **/ g_signal_new ("ge_popup_blocked", EPHY_TYPE_EMBED, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (EphyEmbedIface, popup_blocked), NULL, NULL, ephy_marshal_VOID__STRING_STRING, G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER); /** * EphyEmbed::ge-context-menu: * @embed: * @event: the #EphyEmbedEvent which triggered this signal * * The ::ge_context_menu signal is emitted when a context menu is to be * displayed. This will usually happen when the user right-clicks on a part of * @embed. **/ g_signal_new ("ge_context_menu", EPHY_TYPE_EMBED, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (EphyEmbedIface, context_menu), g_signal_accumulator_true_handled, NULL, ephy_marshal_BOOLEAN__OBJECT, G_TYPE_BOOLEAN, 1, G_TYPE_OBJECT); /** * EphyEmbed::ge-favicon: * @embed: * @address: the URL to @embed's web site's favicon * * The ::ge_favicon signal is emitted when @embed discovers that a favourite * icon (favicon) is available for the site it is visiting. **/ g_signal_new ("ge_favicon", EPHY_TYPE_EMBED, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (EphyEmbedIface, favicon), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); /** * EphyEmbed::ge-location: * @embed: * @address: the new URL @embed is visiting * * The ::ge_location signal is emitted when @embed begins to load a new web * page. For example, if the user clicks on a link or enters an address or if * the previous web page had JavaScript or a META REFRESH tag. * * The ::ge_location signal will be emitted even when @embed is simply * refreshing the same web page. **/ g_signal_new ("ge_location", EPHY_TYPE_EMBED, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (EphyEmbedIface, location), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); /** * EphyEmbed::ge-net-state: * @embed: * @uri: the URI @embed is loading * @state: the #EmbedState of @embed * * The ::ge_net_state signal is emitted when @embed's network negotiation state * changes. For example, this will indicate when page loading is complete or * cancelled. **/ g_signal_new ("ge_net_state", EPHY_TYPE_EMBED, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (EphyEmbedIface, net_state), NULL, NULL, ephy_marshal_VOID__STRING_INT, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT); /** * EphyEmbed::ge-dom-mouse-click: * @embed: * @event: the #EphyEmbedEvent which triggered this signal * * The ::ge_dom_mouse_click signal is emitted when the user clicks in @embed. **/ g_signal_new ("ge_dom_mouse_click", EPHY_TYPE_EMBED, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (EphyEmbedIface, dom_mouse_click), g_signal_accumulator_true_handled, NULL, ephy_marshal_BOOLEAN__OBJECT, G_TYPE_BOOLEAN, 1, G_TYPE_OBJECT); /** * EphyEmbed::ge-dom-mouse-down: * @embed: * @event: the #EphyEmbedEvent which triggered this signal * * The ::ge_dom_mouse_down signal is emitted when the user depresses a mouse * button. **/ g_signal_new ("ge_dom_mouse_down", EPHY_TYPE_EMBED, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (EphyEmbedIface, dom_mouse_down), g_signal_accumulator_true_handled, NULL, ephy_marshal_BOOLEAN__OBJECT, G_TYPE_BOOLEAN, 1, G_TYPE_OBJECT); /** * EphyEmbed::ge-security-change: * @embed: * @level: @embed's new #EmbedSecurityLevel * * The ::ge_security_change signal is emitted when the security level of @embed * changes. For example, this will happen when the user browses from an * insecure website to an SSL-secured one. **/ g_signal_new ("ge_security_change", EPHY_TYPE_EMBED, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (EphyEmbedIface, security_change), NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); /** * EphyEmbed::ge-zoom-change: * @embed: * @zoom: @embed's new zoom level * * The ::ge_zoom_change signal is emitted when @embed's zoom changes. This can * be manual (the user modified the zoom level) or automatic (@embed's zoom is * automatically changed when browsing to a new site for which the user * previously specified a zoom level). * * A @zoom value of 1.0 indicates 100% (normal zoom). **/ g_signal_new ("ge_zoom_change", EPHY_TYPE_EMBED, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (EphyEmbedIface, zoom_change), NULL, NULL, g_cclosure_marshal_VOID__FLOAT, G_TYPE_NONE, 1, G_TYPE_FLOAT); /** * EphyEmbed::ge-content-change: * @embed: * @uri: URI of the new content * * The ::ge_content_change signal is emitted when a new page content * is being loadedinto the browser. It's a good place to do view * related changes, for example to restore the zoom level of a page * or to set an user style sheet. **/ g_signal_new ("ge_content_change", EPHY_TYPE_EMBED, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (EphyEmbedIface, content_change), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); /** * EphyEmbed::ge-modal-alert: * @embed: * * The ::ge-modal-alert signal is emitted when a DOM event will open a * modal alert. * * Return %TRUE to prevent the dialog from being opened. **/ g_signal_new ("ge_modal_alert", EPHY_TYPE_EMBED, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (EphyEmbedIface, modal_alert), g_signal_accumulator_true_handled, NULL, ephy_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN, 0); /** * EphyEmbed::ge-modal-alert-closed: * @embed: * * The ::ge-modal-alert-closed signal is emitted when a modal alert put up by a * DOM event was closed. **/ g_signal_new ("ge_modal_alert_closed", EPHY_TYPE_EMBED, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (EphyEmbedIface, modal_alert_closed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); /** * EphyEmbed::ge-document-type: * @embed: * @type: the new document type * * The ::ge-document-type signal is emitted when @embed determines the type of its document. **/ g_signal_new ("ge_document_type", EPHY_TYPE_EMBED, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (EphyEmbedIface, document_type), NULL, NULL, g_cclosure_marshal_VOID__ENUM, G_TYPE_NONE, 1, EPHY_TYPE_EMBED_DOCUMENT_TYPE); initialized = TRUE; } } /** * ephy_embed_load_url: * @embed: an #EphyEmbed * @url: a URL * * Loads a new web page in @embed. **/ void ephy_embed_load_url (EphyEmbed *embed, const char *url) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); iface->load_url (embed, url); } /** * ephy_embed_stop_load: * @embed: an #EphyEmbed * * If @embed is loading, stops it from continuing. **/ void ephy_embed_stop_load (EphyEmbed *embed) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); iface->stop_load (embed); } /** * ephy_embed_can_go_back: * @embed: an #EphyEmbed * * Return value: %TRUE if @embed can return to a previously-visited location **/ gboolean ephy_embed_can_go_back (EphyEmbed *embed) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); return iface->can_go_back (embed); } /** * ephy_embed_can_go_forward: * @embed: an #EphyEmbed * * Return value: %TRUE if @embed has gone back, and can thus go forward again **/ gboolean ephy_embed_can_go_forward (EphyEmbed *embed) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); return iface->can_go_forward (embed); } /** * ephy_embed_can_go_up: * @embed: an #EphyEmbed * * Returns whether @embed can travel to a higher-level directory on the server. * For example, for http://www.example.com/subdir/index.html, returns %TRUE; for * http://www.example.com/index.html, returns %FALSE. * * Return value: %TRUE if @embed can browse to a higher-level directory **/ gboolean ephy_embed_can_go_up (EphyEmbed *embed) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); return iface->can_go_up (embed); } /** * ephy_embed_get_go_up_list: * @embed: an #EphyEmbed * * Returns a list of (%char *) URLs to higher-level directories on the same * server, in order of deepest to shallowest. For example, given * "http://www.example.com/dir/subdir/file.html", will return a list containing * "http://www.example.com/dir/subdir/", "http://www.example.com/dir/" and * "http://www.example.com/". * * Return value: a list of URLs higher up in @embed's web page's directory * hierarchy **/ GSList * ephy_embed_get_go_up_list (EphyEmbed *embed) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); return iface->get_go_up_list (embed); } /** * ephy_embed_go_back: * @embed: an #EphyEmbed * * Causes @embed to return to the previously-visited web page. **/ void ephy_embed_go_back (EphyEmbed *embed) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); iface->go_back (embed); } /** * ephy_embed_go_forward: * @embed: an #EphyEmbed * * If @embed has returned to a previously-visited web page, proceed forward to * the next page. **/ void ephy_embed_go_forward (EphyEmbed *embed) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); iface->go_forward (embed); } /** * ephy_embed_go_up: * @embed: an #EphyEmbed * * Moves @embed one level up in its web page's directory hierarchy. **/ void ephy_embed_go_up (EphyEmbed *embed) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); iface->go_up (embed); } /** * ephy_embed_get_title: * @embed: an #EphyEmbed * * Return value: the title of the web page displayed in @embed **/ char * ephy_embed_get_title (EphyEmbed *embed) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); return iface->get_title (embed); } /** * ephy_embed_get_location: * @embed: an #EphyEmbed * @toplevel: %FALSE to return the location of the focused frame only * * Returns the URL of the web page displayed in @embed. * * If the web page contains frames, @toplevel will determine which location to * retrieve. If @toplevel is %TRUE, the return value will be the location of the * frameset document. If @toplevel is %FALSE, the return value will be the * location of the currently-focused frame. * * Return value: the URL of the web page displayed in @embed **/ char * ephy_embed_get_location (EphyEmbed *embed, gboolean toplevel) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); return iface->get_location (embed, toplevel); } /** * ephy_embed_get_link_message: * @embed: an #EphyEmbed * * When the user is hovering the mouse over a hyperlink, returns the URL of the * hyperlink. * * Return value: the URL of the link over which the mouse is hovering **/ char * ephy_embed_get_link_message (EphyEmbed *embed) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); return iface->get_link_message (embed); } /** * ephy_embed_get_js_status: * @embed: an #EphyEmbed * * Displays the message JavaScript is attempting to display in the statusbar. * * Note that Epiphany does not display JavaScript statusbar messages. * * Return value: a message from JavaScript meant to be displayed in the * statusbar **/ char * ephy_embed_get_js_status (EphyEmbed *embed) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); return iface->get_js_status (embed); } /** * ephy_embed_reload: * @embed: an #EphyEmbed * @force: %TRUE to bypass cache * * Reloads the web page being displayed in @embed. * * If @force is %TRUE, cache and proxy will be bypassed when * reloading the page. **/ void ephy_embed_reload (EphyEmbed *embed, gboolean force) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); iface->reload (embed, force); } /** * ephy_embed_set_zoom: * @embed: an #EphyEmbed * @zoom: the new zoom level * * Sets the zoom level for a web page. * * Zoom is normally controlled by the Epiphany itself and remembered in * Epiphany's history data. Be very careful not to break this behavior if using * this function; better yet, don't use this function at all. **/ void ephy_embed_set_zoom (EphyEmbed *embed, float zoom) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); iface->set_zoom (embed, zoom); } /** * ephy_embed_get_zoom: * @embed: an #EphyEmbed * * Returns the zoom level of @embed. A zoom of 1.0 corresponds to 100% (normal * size). * * Return value: the zoom level of @embed **/ float ephy_embed_get_zoom (EphyEmbed *embed) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); return iface->get_zoom (embed); } /** * ephy_embed_shistory_n_items: * @embed: an #EphyEmbed * * Returns the number of items in @embed's history. In other words, returns the * number of pages @embed has visited. * * The number is upper-bound by Mozilla's browser.sessionhistory.max_entries * preference. * * Return value: the number of items in @embed's history **/ int ephy_embed_shistory_n_items (EphyEmbed *embed) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); return iface->shistory_n_items (embed); } /** * ephy_embed_shistory_get_nth: * @embed: an #EphyEmbed * @nth: index of the desired page in @embed's browser history * @is_relative: if %TRUE, add @embed's current history position to @nth * @url: returned value of the history entry's URL * @title: returned value of the history entry's title * * Fetches the @url and @title of the @nth item in @embed's session history. * If @is_relative is %TRUE, @nth is an offset from the browser's current * history position. For example, calling this function with @is_relative %TRUE * and @nth %0 will return the URL and title of the current page. **/ void ephy_embed_shistory_get_nth (EphyEmbed *embed, int nth, gboolean is_relative, char **url, char **title) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); iface->shistory_get_nth (embed, nth, is_relative, url, title); } /** * ephy_embed_shistory_get_pos: * @embed: an #EphyEmbed * * Returns @embed's current position in its history. If the user never uses the * "Back" button, this number will be the same as the return value of * ephy_embed_shistory_n_items(). * * Return value: @embed's current position in its history **/ int ephy_embed_shistory_get_pos (EphyEmbed *embed) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); return iface->shistory_get_pos (embed); } /** * ephy_embed_shistory_go_nth: * @embed: an #EphyEmbed * @nth: desired history index * * Opens the webpage at location @nth in @embed's history. **/ void ephy_embed_shistory_go_nth (EphyEmbed *embed, int nth) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); iface->shistory_go_nth (embed, nth); } /** * ephy_embed_get_security_level: * @embed: an #EphyEmbed * @level: return value of security level * @description: return value of the description of the security level * * Fetches the #EmbedSecurityLevel and a newly-allocated string description * of the security state of @embed. **/ void ephy_embed_get_security_level (EphyEmbed *embed, EmbedSecurityLevel *level, char **description) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); iface->get_security_level (embed, level, description); } /** * ephy_embed_show_page_certificate: * @embed: an #EphyEmbed * * Shows a dialogue displaying the certificate of the currently loaded page * of @embed, if it was loaded over a secure connection; else does nothing. **/ void ephy_embed_show_page_certificate (EphyEmbed *embed) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); iface->show_page_certificate (embed); } /** * ephy_embed_find_set_properties: * @embed: an #EphyEmbed * @search_string: the desired search string * @case_sensitive: %TRUE for "case sensitive" to be set * @wrap_around: %TRUE for "wrap around" to be set * * Sets the properties of @embed's "Find" dialog. **/ void ephy_embed_find_set_properties (EphyEmbed *embed, const char *search_string, gboolean case_sensitive, gboolean wrap_around) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); iface->find_set_properties (embed, search_string, case_sensitive, wrap_around); } /** * ephy_embed_find_next: * @embed: an #EphyEmbed * @backwards: %FALSE to search forwards in the document * * Equivalent to pressing "Next" in @embed's Find dialog. * * Return value: %TRUE if a next match was found **/ gboolean ephy_embed_find_next (EphyEmbed *embed, gboolean backwards) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); return iface->find_next (embed, backwards); } /** * ephy_embed_activate: * @embed: an #EphyEmbed * * Gives focus to @embed (i.e., Mozilla). **/ void ephy_embed_activate (EphyEmbed *embed) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); iface->activate (embed); } /** * ephy_embed_set_encoding: * @embed: an #EphyEmbed * @encoding: the desired encoding * * Sets @embed's character encoding to @encoding. These cryptic encoding * strings are listed in embed/ephy-encodings.c. * * Pass an empty string (not NULL) in @encoding to reset @embed to use the * document-specified encoding. **/ void ephy_embed_set_encoding (EphyEmbed *embed, const char *encoding) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); iface->set_encoding (embed, encoding); } /** * ephy_embed_get_encoding: * @embed: an #EphyEmbed * * Returns the @embed's document's encoding **/ char * ephy_embed_get_encoding (EphyEmbed *embed) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); return iface->get_encoding (embed); } /** * ephy_embed_has_automatic_encoding: * @embed: an #EphyEmbed * * Returns whether the @embed's document's was determined by the document itself **/ gboolean ephy_embed_has_automatic_encoding (EphyEmbed *embed) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); return iface->has_automatic_encoding (embed); } /** * ephy_embed_print: * @embed: an #EphyEmbed * * Sends a document to the printer. * **/ void ephy_embed_print (EphyEmbed *embed) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); iface->print (embed); } /** * ephy_embed_set_print_preview_mode: * @embed: an #EphyEmbed * @preview_mode: Whether the print preview mode is enabled. * * Enable and disable the print preview mode. **/ void ephy_embed_set_print_preview_mode (EphyEmbed *embed, gboolean preview_mode) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); iface->set_print_preview_mode (embed, preview_mode); } /** * ephy_embed_print_preview_n_pages: * @embed: an #EphyEmbed * * Returns the number of pages which would appear in @embed's loaded document * if it were to be printed. * * Return value: the number of pages in @embed's loaded document **/ int ephy_embed_print_preview_n_pages (EphyEmbed *embed) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); return iface->print_preview_n_pages (embed); } /** * ephy_embed_print_preview_navigate: * @embed: an #EphyEmbed * @type: an #EphyPrintPreviewNavType which determines where to navigate * @page: if @type is %PRINTPREVIEW_GOTO_PAGENUM, the desired page number * * Navigates @embed's print preview. **/ void ephy_embed_print_preview_navigate (EphyEmbed *embed, EmbedPrintPreviewNavType type, int page) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); iface->print_preview_navigate (embed, type, page); } /** * ephy_embed_has_modified_forms: * @embed: an #EphyEmbed * * Returns %TRUE if the user has modified <input> or <textarea> * values in @embed's loaded document. * * Return value: %TRUE if @embed has user-modified forms **/ gboolean ephy_embed_has_modified_forms (EphyEmbed *embed) { EphyEmbedIface *iface = EPHY_EMBED_GET_IFACE (embed); return iface->has_modified_forms (embed); }