1/*
2 * Copyright (C) 2012 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "core/html/shadow/PasswordGeneratorButtonElement.h"
33
34#include "CSSPropertyNames.h"
35#include "CSSValueKeywords.h"
36#include "core/dom/Event.h"
37#include "core/dom/NodeRenderStyle.h"
38#include "core/dom/shadow/ElementShadow.h"
39#include "core/dom/shadow/ShadowRoot.h"
40#include "core/html/HTMLInputElement.h"
41#include "core/html/shadow/HTMLShadowElement.h"
42#include "core/loader/cache/ImageResource.h"
43#include "core/page/Chrome.h"
44#include "core/page/ChromeClient.h"
45#include "core/page/Page.h"
46#include "core/platform/graphics/Image.h"
47#include "core/rendering/RenderImage.h"
48
49namespace WebCore {
50
51using namespace HTMLNames;
52
53// FIXME: This class is only used in Chromium and has no layout tests.
54
55PasswordGeneratorButtonElement::PasswordGeneratorButtonElement(Document* document)
56    : HTMLDivElement(HTMLNames::divTag, document)
57    , m_isInHoverState(false)
58{
59    setHasCustomStyleCallbacks();
60}
61
62static void getDecorationRootAndDecoratedRoot(HTMLInputElement* input, ShadowRoot*& decorationRoot, ShadowRoot*& decoratedRoot)
63{
64    ShadowRoot* existingRoot = input->youngestShadowRoot();
65    ShadowRoot* newRoot = 0;
66    while (existingRoot->childNodeCount() == 1 && existingRoot->firstChild()->hasTagName(shadowTag)) {
67        newRoot = existingRoot;
68        existingRoot = existingRoot->olderShadowRoot();
69        ASSERT(existingRoot);
70    }
71    if (newRoot) {
72        newRoot->removeChild(newRoot->firstChild());
73    } else {
74        // FIXME: This interacts really badly with author shadow roots because now
75        // we can interleave user agent and author shadow roots on the element meaning
76        // input.shadowRoot may be inaccessible if the browser has decided to decorate
77        // the input.
78        newRoot = input->ensureShadow()->addShadowRoot(input, ShadowRoot::UserAgentShadowRoot);
79    }
80    decorationRoot = newRoot;
81    decoratedRoot = existingRoot;
82}
83
84void PasswordGeneratorButtonElement::decorate(HTMLInputElement* input)
85{
86    ASSERT(input);
87    ShadowRoot* existingRoot;
88    ShadowRoot* decorationRoot;
89    getDecorationRootAndDecoratedRoot(input, decorationRoot, existingRoot);
90    ASSERT(decorationRoot);
91    ASSERT(existingRoot);
92    RefPtr<HTMLDivElement> box = HTMLDivElement::create(input->document());
93    decorationRoot->appendChild(box);
94    box->setInlineStyleProperty(CSSPropertyDisplay, CSSValueFlex);
95    box->setInlineStyleProperty(CSSPropertyAlignItems, CSSValueCenter);
96    ASSERT(existingRoot->childNodeCount() == 1);
97    toHTMLElement(existingRoot->firstChild())->setInlineStyleProperty(CSSPropertyFlexGrow, 1.0, CSSPrimitiveValue::CSS_NUMBER);
98    box->appendChild(HTMLShadowElement::create(HTMLNames::shadowTag, input->document()));
99    setInlineStyleProperty(CSSPropertyDisplay, CSSValueNone);
100    box->appendChild(this);
101}
102
103inline HTMLInputElement* PasswordGeneratorButtonElement::hostInput()
104{
105    // PasswordGeneratorButtonElement is created only by C++ code, and it is always
106    // in <input> shadow.
107    return toHTMLInputElement(shadowHost());
108}
109
110void PasswordGeneratorButtonElement::updateImage()
111{
112    if (!renderer() || !renderer()->isImage())
113        return;
114    RenderImageResource* resource = toRenderImage(renderer())->imageResource();
115    ImageResource* image = m_isInHoverState ? imageForHoverState() : imageForNormalState();
116    ASSERT(image);
117    resource->setImageResource(image);
118}
119
120PassRefPtr<RenderStyle> PasswordGeneratorButtonElement::customStyleForRenderer()
121{
122    RefPtr<RenderStyle> originalStyle = originalStyleForRenderer();
123    RefPtr<RenderStyle> style = RenderStyle::clone(originalStyle.get());
124    RenderStyle* inputStyle = hostInput()->renderStyle();
125    ASSERT(inputStyle);
126    style->setWidth(Length(inputStyle->fontSize(), Fixed));
127    style->setHeight(Length(inputStyle->fontSize(), Fixed));
128    updateImage();
129    return style.release();
130}
131
132RenderObject* PasswordGeneratorButtonElement::createRenderer(RenderStyle*)
133{
134    RenderImage* image = new RenderImage(this);
135    image->setImageResource(RenderImageResource::create());
136    return image;
137}
138
139void PasswordGeneratorButtonElement::attach(const AttachContext& context)
140{
141    HTMLDivElement::attach(context);
142    updateImage();
143}
144
145ImageResource* PasswordGeneratorButtonElement::imageForNormalState()
146{
147    if (!m_cachedImageForNormalState) {
148        RefPtr<Image> image = Image::loadPlatformResource("generatePassword");
149        m_cachedImageForNormalState = new ImageResource(image.get());
150    }
151    return m_cachedImageForNormalState.get();
152}
153
154ImageResource* PasswordGeneratorButtonElement::imageForHoverState()
155{
156    if (!m_cachedImageForHoverState) {
157        RefPtr<Image> image = Image::loadPlatformResource("generatePasswordHover");
158        m_cachedImageForHoverState = new ImageResource(image.get());
159    }
160    return m_cachedImageForHoverState.get();
161}
162
163void PasswordGeneratorButtonElement::defaultEventHandler(Event* event)
164{
165    RefPtr<HTMLInputElement> input = hostInput();
166    if (!input || input->isDisabledOrReadOnly() || !event->isMouseEvent()) {
167        if (!event->defaultHandled())
168            HTMLDivElement::defaultEventHandler(event);
169        return;
170    }
171
172    RefPtr<PasswordGeneratorButtonElement> protector(this);
173    if (event->type() == eventNames().clickEvent) {
174        if (ChromeClient* chromeClient = document()->page() ? document()->page()->chrome().client() : 0)
175            chromeClient->openPasswordGenerator(input.get());
176        event->setDefaultHandled();
177    }
178
179    if (event->type() == eventNames().mouseoverEvent) {
180        m_isInHoverState = true;
181        updateImage();
182    }
183
184    if (event->type() == eventNames().mouseoutEvent) {
185        m_isInHoverState = false;
186        updateImage();
187    }
188
189    if (!event->defaultHandled())
190        HTMLDivElement::defaultEventHandler(event);
191}
192
193bool PasswordGeneratorButtonElement::willRespondToMouseMoveEvents()
194{
195    const HTMLInputElement* input = hostInput();
196    if (!input->isDisabledOrReadOnly())
197        return true;
198
199    return HTMLDivElement::willRespondToMouseMoveEvents();
200}
201
202bool PasswordGeneratorButtonElement::willRespondToMouseClickEvents()
203{
204    const HTMLInputElement* input = hostInput();
205    if (!input->isDisabledOrReadOnly())
206        return true;
207
208    return HTMLDivElement::willRespondToMouseClickEvents();
209}
210
211} // namespace WebCore
212