1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "config.h"
6#include "public/web/WebFrame.h"
7
8#include "core/frame/RemoteFrame.h"
9#include "core/html/HTMLFrameOwnerElement.h"
10#include "platform/UserGestureIndicator.h"
11#include "platform/heap/Handle.h"
12#include "web/OpenedFrameTracker.h"
13#include "web/WebLocalFrameImpl.h"
14#include "web/WebRemoteFrameImpl.h"
15#include <algorithm>
16
17namespace blink {
18
19Frame* toCoreFrame(const WebFrame* frame)
20{
21    if (!frame)
22        return 0;
23
24    return frame->isWebLocalFrame()
25        ? static_cast<Frame*>(toWebLocalFrameImpl(frame)->frame())
26        : toWebRemoteFrameImpl(frame)->frame();
27}
28
29bool WebFrame::swap(WebFrame* frame)
30{
31    using std::swap;
32    RefPtrWillBeRawPtr<Frame> oldFrame = toCoreFrame(this);
33
34    // All child frames must be detached first.
35    oldFrame->detachChildren();
36
37    // If the frame has been detached during detaching its children, return
38    // immediately.
39    // FIXME: There is no unit test for this condition, so one needs to be
40    // written.
41    if (!oldFrame->host())
42        return false;
43
44    // The frame being swapped in should not have a Frame associated
45    // with it yet.
46    ASSERT(!toCoreFrame(frame));
47
48    if (m_parent) {
49        if (m_parent->m_firstChild == this)
50            m_parent->m_firstChild = frame;
51        if (m_parent->m_lastChild == this)
52            m_parent->m_lastChild = frame;
53        swap(m_parent, frame->m_parent);
54    }
55
56    if (m_previousSibling) {
57        m_previousSibling->m_nextSibling = frame;
58        swap(m_previousSibling, frame->m_previousSibling);
59    }
60    if (m_nextSibling) {
61        m_nextSibling->m_previousSibling = frame;
62        swap(m_nextSibling, frame->m_nextSibling);
63    }
64
65    if (m_opener) {
66        m_opener->m_openedFrameTracker->remove(this);
67        m_opener->m_openedFrameTracker->add(frame);
68        swap(m_opener, frame->m_opener);
69    }
70    if (!m_openedFrameTracker->isEmpty()) {
71        m_openedFrameTracker->updateOpener(frame);
72        frame->m_openedFrameTracker.reset(m_openedFrameTracker.release());
73    }
74
75    // Finally, clone the state of the current Frame into one matching
76    // the type of the passed in WebFrame.
77    // FIXME: This is a bit clunky; this results in pointless decrements and
78    // increments of connected subframes.
79    FrameOwner* owner = oldFrame->owner();
80    oldFrame->disconnectOwnerElement();
81    if (frame->isWebLocalFrame()) {
82        toWebLocalFrameImpl(frame)->initializeCoreFrame(oldFrame->host(), owner, oldFrame->tree().name(), nullAtom);
83    } else {
84        toWebRemoteFrameImpl(frame)->initializeCoreFrame(oldFrame->host(), owner, oldFrame->tree().name());
85    }
86
87    return true;
88}
89
90void WebFrame::detach()
91{
92    toCoreFrame(this)->detach();
93}
94
95WebFrame* WebFrame::opener() const
96{
97    return m_opener;
98}
99
100void WebFrame::setOpener(WebFrame* opener)
101{
102    if (m_opener)
103        m_opener->m_openedFrameTracker->remove(this);
104    if (opener)
105        opener->m_openedFrameTracker->add(this);
106    m_opener = opener;
107}
108
109void WebFrame::appendChild(WebFrame* child)
110{
111    // FIXME: Original code asserts that the frames have the same Page. We
112    // should add an equivalent check... figure out what.
113    child->m_parent = this;
114    WebFrame* oldLast = m_lastChild;
115    m_lastChild = child;
116
117    if (oldLast) {
118        child->m_previousSibling = oldLast;
119        oldLast->m_nextSibling = child;
120    } else {
121        m_firstChild = child;
122    }
123
124    toCoreFrame(this)->tree().invalidateScopedChildCount();
125}
126
127void WebFrame::removeChild(WebFrame* child)
128{
129    child->m_parent = 0;
130
131    if (m_firstChild == child)
132        m_firstChild = child->m_nextSibling;
133    else
134        child->m_previousSibling->m_nextSibling = child->m_nextSibling;
135
136    if (m_lastChild == child)
137        m_lastChild = child->m_previousSibling;
138    else
139        child->m_nextSibling->m_previousSibling = child->m_previousSibling;
140
141    child->m_previousSibling = child->m_nextSibling = 0;
142
143    toCoreFrame(this)->tree().invalidateScopedChildCount();
144}
145
146WebFrame* WebFrame::parent() const
147{
148    return m_parent;
149}
150
151WebFrame* WebFrame::top() const
152{
153    WebFrame* frame = const_cast<WebFrame*>(this);
154    for (WebFrame* parent = frame; parent; parent = parent->m_parent)
155        frame = parent;
156    return frame;
157}
158
159WebFrame* WebFrame::firstChild() const
160{
161    return m_firstChild;
162}
163
164WebFrame* WebFrame::lastChild() const
165{
166    return m_lastChild;
167}
168
169WebFrame* WebFrame::previousSibling() const
170{
171    return m_previousSibling;
172}
173
174WebFrame* WebFrame::nextSibling() const
175{
176    return m_nextSibling;
177}
178
179WebFrame* WebFrame::traversePrevious(bool wrap) const
180{
181    if (Frame* frame = toCoreFrame(this))
182        return fromFrame(frame->tree().traversePreviousWithWrap(wrap));
183    return 0;
184}
185
186WebFrame* WebFrame::traverseNext(bool wrap) const
187{
188    if (Frame* frame = toCoreFrame(this))
189        return fromFrame(frame->tree().traverseNextWithWrap(wrap));
190    return 0;
191}
192
193WebFrame* WebFrame::findChildByName(const WebString& name) const
194{
195    Frame* frame = toCoreFrame(this);
196    if (!frame)
197        return 0;
198    // FIXME: It's not clear this should ever be called to find a remote frame.
199    // Perhaps just disallow that completely?
200    return fromFrame(frame->tree().child(name));
201}
202
203WebFrame* WebFrame::fromFrame(Frame* frame)
204{
205    if (!frame)
206        return 0;
207
208    if (frame->isLocalFrame())
209        return WebLocalFrameImpl::fromFrame(toLocalFrame(*frame));
210    return WebRemoteFrameImpl::fromFrame(toRemoteFrame(*frame));
211}
212
213WebFrame::WebFrame()
214    : m_parent(0)
215    , m_previousSibling(0)
216    , m_nextSibling(0)
217    , m_firstChild(0)
218    , m_lastChild(0)
219    , m_opener(0)
220    , m_openedFrameTracker(new OpenedFrameTracker)
221{
222}
223
224WebFrame::~WebFrame()
225{
226    m_openedFrameTracker.reset(0);
227}
228
229void WebFrame::traceChildren(Visitor* visitor, WebFrame* frame)
230{
231#if ENABLE(OILPAN)
232    // Trace the children frames.
233    WebFrame* child = frame ? frame->firstChild() : 0;
234    while (child) {
235        if (child->isWebLocalFrame())
236            visitor->trace(toWebLocalFrameImpl(child));
237        else
238            visitor->trace(toWebRemoteFrameImpl(child));
239
240        child = child->nextSibling();
241    }
242#endif
243}
244
245} // namespace blink
246