SuggestionBoxHandler.cpp [plain text]
#include "config.h"
#include "SuggestionBoxHandler.h"
#include "CSSPropertyNames.h"
#include "CSSValueKeywords.h"
#include "DOMTokenList.h"
#include "ElementShadow.h"
#include "ExceptionCodePlaceholder.h"
#include "HTMLCollection.h"
#include "HTMLDataListElement.h"
#include "HTMLDivElement.h"
#include "HTMLInputElement.h"
#include "HTMLNames.h"
#include "HTMLOptionElement.h"
#include "HTMLSpanElement.h"
#include "RenderBlock.h"
#include "RenderObject.h"
#include "ShadowRoot.h"
#include "StyleResolver.h"
#include "SuggestionBoxElement.h"
#include "Text.h"
#include <BlackBerryPlatformLog.h>
#include <wtf/PassOwnPtr.h>
namespace WebCore {
using namespace HTMLNames;
SuggestionBoxHandler::SuggestionBoxHandler(HTMLInputElement* element)
: m_inputElement(element)
{
}
SuggestionBoxHandler::~SuggestionBoxHandler()
{
hideDropdownBox();
}
PassOwnPtr<SuggestionBoxHandler> SuggestionBoxHandler::create(HTMLInputElement* element)
{
return adoptPtr(new SuggestionBoxHandler(element));
}
void SuggestionBoxHandler::setInputElementAndUpdateDisplay(HTMLInputElement* element, bool allowEmptyPrefix)
{
hideDropdownBox();
m_inputElement = element;
showDropdownBox(allowEmptyPrefix);
}
void SuggestionBoxHandler::showDropdownBox(bool allowEmptyPrefix)
{
m_suggestions.clear();
parseSuggestions(allowEmptyPrefix);
if (!m_dropdownBox) {
if (!m_suggestions.size())
return;
buildDropdownBoxTree();
}
insertSuggestionsToDropdownBox();
}
void SuggestionBoxHandler::hideDropdownBox()
{
if (m_dropdownBox) {
if (ShadowRoot* shadowRoot = m_inputElement->userAgentShadowRoot()) {
ExceptionCode ec;
shadowRoot->removeChild(m_dropdownBox.get(), ec);
}
m_dropdownBox = 0;
}
m_suggestions.clear();
}
void SuggestionBoxHandler::changeInputElementInnerTextValue(String& text)
{
m_inputElement->setInnerTextValue(text);
}
void SuggestionBoxHandler::parseSuggestions(bool allowEmptyPrefix)
{
if (!m_inputElement)
return;
HTMLDataListElement* dList = m_inputElement->dataList();
if (!dList)
return;
RefPtr<HTMLCollection> optionsList = dList->options();
String prefix = m_inputElement->innerTextValue();
if (!optionsList->length() || (!allowEmptyPrefix && prefix.isEmpty())) {
hideDropdownBox();
return;
}
for (unsigned i = 0; i < optionsList->length(); i++) {
HTMLOptionElement* target = toHTMLOptionElement((optionsList->item(i)));
if (!m_inputElement->isValidValue(target->value()))
continue;
String candidateString = m_inputElement->sanitizeValue(target->value());
if (!candidateString.findIgnoringCase(prefix) && candidateString.length() <= (unsigned) m_inputElement->maxLength())
m_suggestions.append(candidateString);
}
}
void SuggestionBoxHandler::insertSuggestionsToDropdownBox()
{
ASSERT(m_dropdownBox);
Document* document = m_dropdownBox->document();
m_dropdownBox->removeChildren();
int prefixIndex = m_inputElement->innerTextValue().length();
unsigned total = m_suggestions.size();
for (unsigned i = 0; i < total; ++i) {
RefPtr<HTMLElement> suggestionItem = SuggestionBoxElement::create(this, m_suggestions[i], document);
if (i < total - 1)
suggestionItem->setPseudo("-webkit-suggestion-dropdown-box-item");
else
suggestionItem->setPseudo("-webkit-suggestion-dropdown-box-item-last");
RefPtr<HTMLSpanElement> prefixElement = HTMLSpanElement::create(HTMLNames::spanTag, document);
prefixElement->appendChild(Text::create(document, m_suggestions[i].substring(0, prefixIndex)));
prefixElement->setPseudo("-webkit-suggestion-prefix-text");
suggestionItem->appendChild(prefixElement.release(), ASSERT_NO_EXCEPTION);
suggestionItem->appendChild(Text::create(document, m_suggestions[i].substring(prefixIndex)), ASSERT_NO_EXCEPTION);
m_dropdownBox->appendChild(suggestionItem.release(), ASSERT_NO_EXCEPTION);
}
}
static void adjustDropdownBoxPosition(const LayoutRect& hostRect, HTMLElement* dropdownBox)
{
ASSERT(dropdownBox);
if (hostRect.isEmpty())
return;
double hostX = hostRect.x();
double hostY = hostRect.y();
if (RenderObject* renderer = dropdownBox->renderer()) {
if (RenderBox* container = renderer->containingBlock()) {
FloatPoint containerLocation = container->localToAbsolute();
hostX -= containerLocation.x() + container->borderLeft();
hostY -= containerLocation.y() + container->borderTop();
}
}
dropdownBox->setInlineStyleProperty(CSSPropertyTop, hostY + hostRect.height(), CSSPrimitiveValue::CSS_PX);
const int bubbleArrowTopOffset = 32;
double bubbleX = hostX;
if (hostRect.width() / 2 < bubbleArrowTopOffset)
bubbleX = max(hostX + hostRect.width() / 2 - bubbleArrowTopOffset, 0.0);
dropdownBox->setInlineStyleProperty(CSSPropertyLeft, bubbleX, CSSPrimitiveValue::CSS_PX);
}
void SuggestionBoxHandler::buildDropdownBoxTree()
{
ShadowRoot* shadowRoot = m_inputElement->ensureUserAgentShadowRoot();
if (!shadowRoot)
return;
Document* document = m_inputElement->document();
m_dropdownBox = HTMLDivElement::create(document);
m_dropdownBox->setPseudo("-webkit-suggestion-dropdown-box");
m_dropdownBox->setInlineStyleProperty(CSSPropertyPosition, CSSValueAbsolute);
m_dropdownBox->setInlineStyleProperty(CSSPropertyWidth, String::number(m_inputElement->boundingBox().width().rawValue() - 2) + "px", false);
ExceptionCode ec = 0;
shadowRoot->appendChild(m_dropdownBox.get(), ec);
ASSERT(!ec);
document->updateLayout();
adjustDropdownBoxPosition(m_inputElement->boundingBox(), m_dropdownBox.get());
}
}