/* * This file is part of the DOM implementation for KDE. * * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller (mueller@kde.org) * (C) 2004, 2005, 2006 Apple Computer, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #undef FORMS_DEBUG //#define FORMS_DEBUG #include "html/html_formimpl.h" #include "khtmlview.h" #include "khtml_part.h" #include "html/html_documentimpl.h" #include "khtml_settings.h" #include "misc/htmlhashes.h" #include "css/cssstyleselector.h" #include "css/cssproperties.h" #include "css/cssvalues.h" #include "css/csshelper.h" #include "xml/dom_textimpl.h" #include "xml/dom_docimpl.h" #include "xml/dom2_eventsimpl.h" #include "xml/dom_restyler.h" #include "khtml_ext.h" #include "rendering/render_form.h" #include #include #include #include #include #include #include #ifndef KHTML_NO_WALLET #include #endif #include #include #include #include #include // for keygen #include #include #include using namespace DOM; using namespace khtml; HTMLFormElementImpl::HTMLFormElementImpl(DocumentImpl *doc, bool implicit) : HTMLElementImpl(doc) { m_implicit = implicit; m_post = false; m_multipart = false; m_autocomplete = true; m_insubmit = false; m_doingsubmit = false; m_inreset = false; m_enctype = "application/x-www-form-urlencoded"; m_boundary = "----------" + TDEApplication::randomString( 42 + 13 ); m_acceptcharset = "UNKNOWN"; m_malformed = false; } HTMLFormElementImpl::~HTMLFormElementImpl() { if (getDocument() && getDocument()->view() && getDocument()->view()->part()) { getDocument()->view()->part()->dequeueWallet(this); } TQPtrListIterator it(formElements); for (; it.current(); ++it) it.current()->m_form = 0; TQPtrListIterator it2(imgElements); for (; it2.current(); ++it2) it2.current()->m_form = 0; } NodeImpl::Id HTMLFormElementImpl::id() const { return ID_FORM; } long HTMLFormElementImpl::length() const { int len = 0; TQPtrListIterator it(formElements); for (; it.current(); ++it) if (it.current()->isEnumeratable()) ++len; return len; } static TQCString encodeCString(const TQCString& e) { // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1 // safe characters like NS handles them for compatibility static const char *safe = "-._*"; TQCString encoded(( e.length()+e.contains( '\n' ) )*3 +e.contains('\r') * 3 + 1); int enclen = 0; bool crmissing = false; unsigned char oldc; unsigned char c ='\0'; //TQCString orig(e.data(), e.size()); unsigned len = e.length(); for(unsigned pos = 0; pos < len; pos++) { oldc = c; c = e[pos]; if (crmissing && c != '\n') { encoded[enclen++] = '%'; encoded[enclen++] = '0'; encoded[enclen++] = 'D'; crmissing = false; } if ( (( c >= 'A') && ( c <= 'Z')) || (( c >= 'a') && ( c <= 'z')) || (( c >= '0') && ( c <= '9')) || (strchr(safe, c)) ) encoded[enclen++] = c; else if ( c == ' ' ) encoded[enclen++] = '+'; else if ( c == '\n' ) { encoded[enclen++] = '%'; encoded[enclen++] = '0'; encoded[enclen++] = 'D'; encoded[enclen++] = '%'; encoded[enclen++] = '0'; encoded[enclen++] = 'A'; crmissing = false; } else if (c == '\r' && oldc != '\n') { crmissing = true; } else if ( c != '\r' ) { encoded[enclen++] = '%'; unsigned int h = c / 16; h += (h > 9) ? ('A' - 10) : '0'; encoded[enclen++] = h; unsigned int l = c % 16; l += (l > 9) ? ('A' - 10) : '0'; encoded[enclen++] = l; } } encoded[enclen++] = '\0'; encoded.truncate(enclen); return encoded; } // ### This function only encodes to numeric ampersand escapes, // ### we could use standard ampersand values as well. inline static TQString escapeUnencodeable(const TQTextCodec* codec, const TQString& s) { TQString enc_string; const int len = s.length(); for(int i=0; i canEncode(c)) enc_string.append(c); else { TQString ampersandEscape; ampersandEscape.sprintf("&#%u;", c.unicode()); enc_string.append(ampersandEscape); } } return enc_string; } inline static TQCString fixUpfromUnicode(const TQTextCodec* codec, const TQString& s) { TQCString str = codec->fromUnicode(escapeUnencodeable(codec,s)); str.truncate(str.length()); return str; } TQByteArray HTMLFormElementImpl::formData(bool& ok) { #ifdef FORMS_DEBUG kdDebug( 6030 ) << "form: formData()" << endl; #endif TQByteArray form_data(0); TQCString enc_string = ""; // used for non-multipart data // find out the QTextcodec to use const TQString str = m_acceptcharset.string(); const TQChar space(' '); const unsigned int strLength = str.length(); for(unsigned int i=0; i < strLength; ++i) if(str[i].latin1() == ',') str[i] = space; const TQStringList charsets = TQStringList::split(' ', str); TQTextCodec* codec = 0; KHTMLView *view = getDocument()->view(); { TQStringList::ConstIterator it = charsets.begin(); const TQStringList::ConstIterator itEnd = charsets.end(); for ( ; it != itEnd; ++it ) { TQString enc = (*it); if(enc.contains("UNKNOWN")) { // use standard document encoding enc = "ISO 8859-1"; if(view && view->part()) enc = view->part()->encoding(); } if((codec = KGlobal::charsets()->codecForName(enc.latin1()))) break; } } if(!codec) codec = TQTextCodec::codecForLocale(); // we need to map visual hebrew to logical hebrew, as the web // server alsways expects responses in logical ordering if ( codec->mibEnum() == 11 ) codec = TQTextCodec::codecForMib( 85 ); m_encCharset = codec->name(); const unsigned int m_encCharsetLength = m_encCharset.length(); for(unsigned int i=0; i < m_encCharsetLength; ++i) m_encCharset[i] = m_encCharset[i].latin1() == ' ' ? TQChar('-') : m_encCharset[i].lower(); TQStringList fileUploads, fileNotUploads; for (TQPtrListIterator it(formElements); it.current(); ++it) { HTMLGenericFormElementImpl* const current = it.current(); khtml::encodingList lst; if (!current->disabled() && current->encoding(codec, lst, m_multipart)) { //kdDebug(6030) << "adding name '" << current->name().string() << "'" << endl; khtml::encodingList::ConstIterator it = lst.begin(); const khtml::encodingList::ConstIterator itEnd = lst.end(); for( it = lst.begin(); it != itEnd; ++it ) { if (!m_multipart) { // handle ISINDEX / special // but only if its the first entry if ( enc_string.isEmpty() && *it == "isindex" ) { ++it; enc_string += encodeCString( *it ); } else { if(!enc_string.isEmpty()) enc_string += '&'; enc_string += encodeCString(*it); enc_string += "="; ++it; enc_string += encodeCString(*it); } } else { TQCString hstr("--"); hstr += m_boundary.latin1(); hstr += "\r\n"; hstr += "Content-Disposition: form-data; name=\""; hstr += (*it).data(); hstr += "\""; // if the current type is FILE, then we also need to // include the filename if (current->id() == ID_INPUT && static_cast(current)->inputType() == HTMLInputElementImpl::FILE && current->renderer()) { KURL path; TQString val = static_cast(current)->value().string().stripWhiteSpace(); if (!val.isEmpty() && TQDir::isRelativePath(val) && TQFile::exists(KGlobalSettings::documentPath() + val)) { path.setPath(KGlobalSettings::documentPath() + val); } else { path = KURL::fromPathOrURL(val); } hstr += fixUpfromUnicode(codec, "; filename=\"" + path.fileName() + "\""); if (path.isValid()) { fileUploads << path.prettyURL(0, KURL::StripFileProtocol); const KMimeType::Ptr ptr = KMimeType::findByURL(path); if (!ptr->name().isEmpty()) { hstr += "\r\nContent-Type: "; hstr += ptr->name().ascii(); } } else if (!val.isEmpty()) { fileNotUploads << path.prettyURL(0, KURL::StripFileProtocol); } } hstr += "\r\n\r\n"; ++it; // append body const unsigned int old_size = form_data.size(); form_data.resize( old_size + hstr.length() + (*it).size() + 1); memcpy(form_data.data() + old_size, hstr.data(), hstr.length()); memcpy(form_data.data() + old_size + hstr.length(), *it, (*it).size()); form_data[form_data.size()-2] = '\r'; form_data[form_data.size()-1] = '\n'; // reset unsubmittedFormChange flag if (current->id() == ID_INPUT && static_cast(current)->inputType() == HTMLInputElementImpl::TEXT) static_cast(current)->setUnsubmittedFormChange(false); if (current->id() == ID_TEXTAREA) static_cast(current)->setUnsubmittedFormChange(false); } } } } if (fileNotUploads.count()) { const int result = KMessageBox::warningContinueCancelList( 0, i18n("The following files will not be uploaded" " because they could not be found.\n" "Do you want to continue?"), fileNotUploads, i18n("Submit Confirmation"),KGuiItem(i18n("&Submit Anyway"))); if (result == KMessageBox::Cancel) { ok = false; return TQByteArray(); } } if (fileUploads.count()) { const int result = KMessageBox::warningContinueCancelList( 0, i18n("You're about to transfer the following files from " "your local computer to the Internet.\n" "Do you really want to continue?"), fileUploads, i18n("Send Confirmation"),KGuiItem(i18n("&Send Files"))); if (result == KMessageBox::Cancel) { ok = false; return TQByteArray(); } } if (m_multipart) enc_string = ("--" + m_boundary + "--\r\n").ascii(); const int old_size = form_data.size(); form_data.resize( form_data.size() + enc_string.length() ); memcpy(form_data.data() + old_size, enc_string.data(), enc_string.length() ); ok = true; return form_data; } void HTMLFormElementImpl::setEnctype( const DOMString& type ) { if(type.string().find("multipart", 0, false) != -1 || type.string().find("form-data", 0, false) != -1) { m_enctype = "multipart/form-data"; m_multipart = true; m_post = true; } else if (type.string().find("text", 0, false) != -1 || type.string().find("plain", 0, false) != -1) { m_enctype = "text/plain"; m_multipart = false; } else { m_enctype = "application/x-www-form-urlencoded"; m_multipart = false; } m_encCharset = TQString::null; } static TQString calculateAutoFillKey(const HTMLFormElementImpl& e) { KURL k(e.getDocument()->URL()); k.setRef(TQString::null); k.setQuery(TQString::null); // ensure that we have the user / password inside the url // otherwise we might have a potential security problem // by saving passwords under wrong lookup key. const TQString name = e.getAttribute(ATTR_NAME).string().stripWhiteSpace(); const TQRegExp re("[;,!]"); const TQStringList url = TQStringList::split(re, k.url()); return url[0] + '#' + name; } void HTMLFormElementImpl::doAutoFill() { #ifndef KHTML_NO_WALLET const TQString key = calculateAutoFillKey(*this); if (KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(), KWallet::Wallet::FormDataFolder(), key)) return; // assert(view()) getDocument()->view()->part()->openWallet(this); #endif // KHTML_NO_WALLET } void HTMLFormElementImpl::walletOpened(KWallet::Wallet *w) { #ifndef KHTML_NO_WALLET assert(w); const TQString key = calculateAutoFillKey(*this); if (!w->hasFolder(KWallet::Wallet::FormDataFolder())) { return; // failed } w->setFolder(KWallet::Wallet::FormDataFolder()); TQMap map; if (w->readMap(key, map)) return; // failed, abort for (TQPtrListIterator it(formElements); it.current(); ++it) { if (it.current()->id() == ID_INPUT) { HTMLInputElementImpl* const current = static_cast(it.current()); if ((current->inputType() == HTMLInputElementImpl::PASSWORD || current->inputType() == HTMLInputElementImpl::TEXT) && !current->readOnly() && map.contains(current->name().string())) { getDocument()->setFocusNode(current); current->setValue(map[current->name().string()]); } } } #endif // KHTML_NO_WALLET } void HTMLFormElementImpl::submitFromKeyboard() { // Activate the first nondisabled submit button // if there is none, do a submit anyway if not more // than one or unsigned int inputtext = 0; for (TQPtrListIterator it(formElements); it.current(); ++it) { if (it.current()->id() == ID_BUTTON) { HTMLButtonElementImpl* const current = static_cast(it.current()); if (current->buttonType() == HTMLButtonElementImpl::SUBMIT && !current->disabled()) { current->click(); return; } } else if (it.current()->id() == ID_INPUT) { HTMLInputElementImpl* const current = static_cast(it.current()); switch(current->inputType()) { case HTMLInputElementImpl::SUBMIT: case HTMLInputElementImpl::IMAGE: if(!current->disabled()) { current->click(); return; } break; case HTMLInputElementImpl::TEXT: case HTMLInputElementImpl::PASSWORD: ++inputtext; default: break; } } } if (inputtext <= 1) prepareSubmit(); } void HTMLFormElementImpl::gatherWalletData() { #ifndef KHTML_NO_WALLET KHTMLView* const view = getDocument()->view(); // check if we have any password input's m_walletMap.clear(); m_havePassword = false; m_haveTextarea = false; const KURL formUrl(getDocument()->URL()); if (view && !view->nonPasswordStorableSite(formUrl.host())) { for (TQPtrListIterator it(formElements); it.current(); ++it) { if (it.current()->id() == ID_INPUT) { HTMLInputElementImpl* const c = static_cast (it.current()); if ((c->inputType() == HTMLInputElementImpl::TEXT || c->inputType() == HTMLInputElementImpl::PASSWORD) && !c->readOnly()) { m_walletMap.insert(c->name().string(), c->value().string()); if (c->inputType() == HTMLInputElementImpl::PASSWORD && !c->value().isEmpty()) m_havePassword = true; } } else if (it.current()->id() == ID_TEXTAREA) m_haveTextarea = true; } } #endif // KHTML_NO_WALLET } bool HTMLFormElementImpl::prepareSubmit() { KHTMLView* const view = getDocument()->view(); if(m_insubmit || !view || !view->part() || view->part()->onlyLocalReferences()) return m_insubmit; gatherWalletData(); m_insubmit = true; m_doingsubmit = false; if ( dispatchHTMLEvent(EventImpl::SUBMIT_EVENT,true,true) && !m_doingsubmit ) m_doingsubmit = true; m_insubmit = false; if ( m_doingsubmit ) submit(); return m_doingsubmit; } void HTMLFormElementImpl::submit( ) { if ( m_insubmit ) { m_doingsubmit = true; return; } m_insubmit = true; #ifdef FORMS_DEBUG kdDebug( 6030 ) << "submitting!" << endl; #endif bool ok; KHTMLView* const view = getDocument()->view(); const TQByteArray form_data = formData(ok); const KURL formUrl(getDocument()->URL()); if (ok && view) { if (m_walletMap.isEmpty()) { gatherWalletData(); } #ifndef KHTML_NO_WALLET if (m_havePassword && !m_haveTextarea && KWallet::Wallet::isEnabled()) { const TQString key = calculateAutoFillKey(*this); const bool doesnotexist = KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(), KWallet::Wallet::FormDataFolder(), key); KWallet::Wallet* const w = view->part()->wallet(); bool login_changed = false; if (!doesnotexist && w) { // check if the login information changed from what // we had so far. if (w->hasFolder(KWallet::Wallet::FormDataFolder())) { w->setFolder(KWallet::Wallet::FormDataFolder()); TQMap map; if (!w->readMap(key, map)) { TQMapConstIterator it = map.begin(); const TQMapConstIterator itEnd = map.end(); for ( ; it != itEnd; ++it ) if ( map[it.key()] != m_walletMap[it.key()] ) { login_changed = true; break; } } else { login_changed = true; } } } if ( doesnotexist || !w || login_changed ) { // TODO use KMessageBox::questionYesNoCancel() again, if you can pass a KGuiItem for Cancel KDialogBase* const dialog = new KDialogBase(i18n("Save Login Information"), KDialogBase::Yes | KDialogBase::No | KDialogBase::Cancel, KDialogBase::Yes, KDialogBase::Cancel, 0, "questionYesNoCancel", true, true, i18n("Store"), KGuiItem(i18n("Ne&ver for This Site")), i18n("Do Not Store")); bool checkboxResult = false; const int savePassword = KMessageBox::createKMessageBox(dialog, TQMessageBox::Information, i18n("Store passwords on this page?"), TQStringList(), TQString::null, &checkboxResult, KMessageBox::Notify); if ( savePassword == KDialogBase::Yes ) { // ensure that we have the user / password inside the url // otherwise we might have a potential security problem // by saving passwords under wrong lookup key. if (view->part()) { view->part()->saveToWallet(key, m_walletMap); } } else if ( savePassword == KDialogBase::No ) { view->addNonPasswordStorableSite(formUrl.host()); } } } #endif // KHTML_NO_WALLET const DOMString url(khtml::parseURL(getAttribute(ATTR_ACTION))); if(m_post) { view->part()->submitForm( "post", url.string(), form_data, m_target.string(), enctype().string(), m_boundary ); } else { view->part()->submitForm( "get", url.string(), form_data, m_target.string() ); } } m_walletMap.clear(); // done with it m_havePassword = m_haveTextarea= false; m_doingsubmit = m_insubmit = false; } void HTMLFormElementImpl::reset( ) { KHTMLView* const view = getDocument()->view(); if(m_inreset || !view || !view->part()) return; m_inreset = true; #ifdef FORMS_DEBUG kdDebug( 6030 ) << "reset pressed!" << endl; #endif // ### DOM2 labels this event as not cancelable, however // common browsers( sick! ) allow it be cancelled. if ( !dispatchHTMLEvent(EventImpl::RESET_EVENT,true, true) ) { m_inreset = false; return; } for (TQPtrListIterator it(formElements); it.current(); ++it) it.current()->reset(); m_inreset = false; } void HTMLFormElementImpl::parseAttribute(AttributeImpl *attr) { switch(attr->id()) { case ATTR_ACTION: break; case ATTR_TARGET: m_target = attr->value(); break; case ATTR_METHOD: m_post = ( strcasecmp( attr->value(), "post" ) == 0 ); break; case ATTR_ENCTYPE: setEnctype( attr->value() ); break; case ATTR_ACCEPT_CHARSET: // space separated list of charsets the server // accepts - see rfc2045 m_acceptcharset = attr->value(); break; case ATTR_ACCEPT: // ignore this one for the moment... break; case ATTR_AUTOCOMPLETE: m_autocomplete = strcasecmp( attr->value(), "off" ); break; case ATTR_ONSUBMIT: setHTMLEventListener(EventImpl::SUBMIT_EVENT, getDocument()->createHTMLEventListener(attr->value().string(), "onsubmit", this)); break; case ATTR_ONRESET: setHTMLEventListener(EventImpl::RESET_EVENT, getDocument()->createHTMLEventListener(attr->value().string(), "onreset", this)); break; case ATTR_NAME: if (inDocument() && m_name != attr->value()) { getDocument()->underDocNamedCache().remove(m_name.string(), this); getDocument()->underDocNamedCache().add (attr->value().string(), this); } m_name = attr->value(); //Fallthrough intentional default: HTMLElementImpl::parseAttribute(attr); } } void HTMLFormElementImpl::removedFromDocument() { getDocument()->underDocNamedCache().remove(m_name.string(), this); HTMLElementImpl::removedFromDocument(); } void HTMLFormElementImpl::insertedIntoDocument() { getDocument()->underDocNamedCache().add(m_name.string(), this); HTMLElementImpl::insertedIntoDocument(); } void HTMLFormElementImpl::removeId(const TQString& id) { getDocument()->underDocNamedCache().remove(id, this); HTMLElementImpl::removeId(id); } void HTMLFormElementImpl::addId (const TQString& id) { getDocument()->underDocNamedCache().add(id, this); HTMLElementImpl::addId(id); } void HTMLFormElementImpl::radioClicked( HTMLGenericFormElementImpl *caller ) { for (TQPtrListIterator it(formElements); it.current(); ++it) { HTMLGenericFormElementImpl* const current = it.current(); if (current->id() == ID_INPUT && static_cast(current)->inputType() == HTMLInputElementImpl::RADIO && current != caller && current->form() == caller->form() && current->name() == caller->name()) static_cast(current)->setChecked(false); } } void HTMLFormElementImpl::registerFormElement(HTMLGenericFormElementImpl *e) { formElements.append(e); } void HTMLFormElementImpl::removeFormElement(HTMLGenericFormElementImpl *e) { formElements.remove(e); } void HTMLFormElementImpl::registerImgElement(HTMLImageElementImpl *e) { imgElements.append(e); } void HTMLFormElementImpl::removeImgElement(HTMLImageElementImpl *e) { imgElements.remove(e); } // ------------------------------------------------------------------------- HTMLGenericFormElementImpl::HTMLGenericFormElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f) : HTMLElementImpl(doc) { m_disabled = m_readOnly = false; m_name = 0; if (f) m_form = f; else m_form = getForm(); if (m_form) m_form->registerFormElement(this); } void HTMLGenericFormElementImpl::insertedIntoDocument() { HTMLElementImpl::insertedIntoDocument(); if (!m_form) { HTMLFormElementImpl* const newform = getForm(); if (newform) { m_form = newform; m_form->registerFormElement(this); } } } void HTMLGenericFormElementImpl::removedFromDocument() { HTMLElementImpl::removedFromDocument(); if (m_form) m_form->removeFormElement(this); m_form = 0; } HTMLGenericFormElementImpl::~HTMLGenericFormElementImpl() { if (m_form) m_form->removeFormElement(this); if (m_name) m_name->deref(); } void HTMLGenericFormElementImpl::parseAttribute(AttributeImpl *attr) { switch(attr->id()) { case ATTR_DISABLED: setDisabled( attr->val() != 0 ); break; case ATTR_READONLY: { const bool m_oldreadOnly = m_readOnly; m_readOnly = attr->val() != 0; if (m_oldreadOnly != m_readOnly) setChanged(); break; } default: HTMLElementImpl::parseAttribute(attr); } } void HTMLGenericFormElementImpl::attach() { assert(!attached()); if (m_render) { assert(m_render->style()); parentNode()->renderer()->addChild(m_render, nextRenderer()); } // FIXME: This handles the case of a new form element being created by // JavaScript and inserted inside a form. What it does not handle is // a form element being moved from inside a form to outside, or from one // inside one form to another. The reason this other case is hard to fix // is that during parsing, we may have been passed a form that we are not // inside, DOM-tree-wise. If so, it's hard for us to know when we should // be removed from that form's element list. if (!m_form) { m_form = getForm(); if (m_form) m_form->registerFormElement(this); } NodeBaseImpl::attach(); // The call to updateFromElement() needs to go after the call through // to the base class's attach() because that can sometimes do a close // on the renderer. if (m_render) m_render->updateFromElement(); } HTMLFormElementImpl *HTMLGenericFormElementImpl::getForm() const { NodeImpl *p = parentNode(); while(p) { if( p->id() == ID_FORM ) return static_cast(p); if( p->parentNode() && p->parentNode()->id() == ID_TABLE && p->previousSibling() ) { p = p->previousSibling(); continue; } p = p->parentNode(); } #ifdef FORMS_DEBUG kdDebug( 6030 ) << "couldn't find form!" << endl; kdDebug( 6030 ) << kdBacktrace() << endl; #endif return 0; } DOMString HTMLGenericFormElementImpl::name() const { if (m_name) return m_name; // ### // DOMString n = getDocument()->htmlMode() != DocumentImpl::XHtml ? // getAttribute(ATTR_NAME) : getAttribute(ATTR_ID); const DOMString n = getAttribute(ATTR_NAME); if (n.isNull()) return new DOMStringImpl(""); return n; } void HTMLGenericFormElementImpl::setName(const DOMString& name) { if (m_name) m_name->deref(); m_name = name.implementation(); setAttribute( ATTR_NAME, name ); if (m_name) m_name->ref(); } void HTMLGenericFormElementImpl::onSelect() { // ### make this work with new form events architecture dispatchHTMLEvent(EventImpl::SELECT_EVENT,true,false); } void HTMLGenericFormElementImpl::onChange() { // ### make this work with new form events architecture dispatchHTMLEvent(EventImpl::CHANGE_EVENT,true,false); } void HTMLGenericFormElementImpl::setDisabled( bool _disabled ) { if ( m_disabled != _disabled ) { m_disabled = _disabled; // Trigger dynamic restyles getDocument()->dynamicDomRestyler().restyleDepedent(this, OtherStateDependency); // We need to update rendering under all circumstances if (!changed() && m_render) { m_render->updateFromElement(); } } } bool HTMLGenericFormElementImpl::isFocusable() const { if (disabled()) return false; //Non-widget INPUT TYPE="image" and