1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4 *           (C) 2001 Dirk Mueller (mueller@kde.org)
5 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
6 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
7 * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
8 * Copyright (C) 2011 Google Inc. All rights reserved.
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 * Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB.  If not, write to
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24 */
25#include "config.h"
26#include "core/dom/TreeScopeAdopter.h"
27
28#include "core/accessibility/AXObjectCache.h"
29#include "core/dom/Attr.h"
30#include "core/dom/NodeRareData.h"
31#include "core/dom/NodeTraversal.h"
32#include "core/dom/shadow/ElementShadow.h"
33#include "core/dom/shadow/ShadowRoot.h"
34
35namespace blink {
36
37void TreeScopeAdopter::moveTreeToNewScope(Node& root) const
38{
39    ASSERT(needsScopeChange());
40
41#if !ENABLE(OILPAN)
42    oldScope().guardRef();
43#endif
44
45    // If an element is moved from a document and then eventually back again the collection cache for
46    // that element may contain stale data as changes made to it will have updated the DOMTreeVersion
47    // of the document it was moved to. By increasing the DOMTreeVersion of the donating document here
48    // we ensure that the collection cache will be invalidated as needed when the element is moved back.
49    Document& oldDocument = oldScope().document();
50    Document& newDocument = newScope().document();
51    bool willMoveToNewDocument = oldDocument != newDocument;
52    AXObjectCache* axObjectCache = oldDocument.existingAXObjectCache();
53    if (willMoveToNewDocument)
54        oldDocument.incDOMTreeVersion();
55
56    for (Node* node = &root; node; node = NodeTraversal::next(*node, &root)) {
57        updateTreeScope(*node);
58
59        if (willMoveToNewDocument) {
60            if (axObjectCache)
61                axObjectCache->remove(node);
62            moveNodeToNewDocument(*node, oldDocument, newDocument);
63        } else if (node->hasRareData()) {
64            NodeRareData* rareData = node->rareData();
65            if (rareData->nodeLists())
66                rareData->nodeLists()->adoptTreeScope();
67        }
68
69        if (!node->isElementNode())
70            continue;
71
72        if (node->hasSyntheticAttrChildNodes()) {
73            WillBeHeapVector<RefPtrWillBeMember<Attr> >& attrs = *toElement(node)->attrNodeList();
74            for (unsigned i = 0; i < attrs.size(); ++i)
75                moveTreeToNewScope(*attrs[i]);
76        }
77
78        for (ShadowRoot* shadow = node->youngestShadowRoot(); shadow; shadow = shadow->olderShadowRoot()) {
79            shadow->setParentTreeScope(newScope());
80            if (willMoveToNewDocument)
81                moveTreeToNewDocument(*shadow, oldDocument, newDocument);
82        }
83    }
84
85#if !ENABLE(OILPAN)
86    oldScope().guardDeref();
87#endif
88}
89
90void TreeScopeAdopter::moveTreeToNewDocument(Node& root, Document& oldDocument, Document& newDocument) const
91{
92    ASSERT(oldDocument != newDocument);
93    for (Node* node = &root; node; node = NodeTraversal::next(*node, &root)) {
94        moveNodeToNewDocument(*node, oldDocument, newDocument);
95
96        if (node->hasSyntheticAttrChildNodes()) {
97            WillBeHeapVector<RefPtrWillBeMember<Attr> >& attrs = *toElement(node)->attrNodeList();
98            for (unsigned i = 0; i < attrs.size(); ++i)
99                moveTreeToNewDocument(*attrs[i], oldDocument, newDocument);
100        }
101
102        for (ShadowRoot* shadow = node->youngestShadowRoot(); shadow; shadow = shadow->olderShadowRoot())
103            moveTreeToNewDocument(*shadow, oldDocument, newDocument);
104    }
105}
106
107#if ENABLE(ASSERT)
108static bool didMoveToNewDocumentWasCalled = false;
109static Document* oldDocumentDidMoveToNewDocumentWasCalledWith = 0;
110
111void TreeScopeAdopter::ensureDidMoveToNewDocumentWasCalled(Document& oldDocument)
112{
113    ASSERT(!didMoveToNewDocumentWasCalled);
114    ASSERT_UNUSED(oldDocument, oldDocument == oldDocumentDidMoveToNewDocumentWasCalledWith);
115    didMoveToNewDocumentWasCalled = true;
116}
117#endif
118
119inline void TreeScopeAdopter::updateTreeScope(Node& node) const
120{
121    ASSERT(!node.isTreeScope());
122    ASSERT(node.treeScope() == oldScope());
123#if !ENABLE(OILPAN)
124    newScope().guardRef();
125    oldScope().guardDeref();
126#endif
127    node.setTreeScope(m_newScope);
128}
129
130inline void TreeScopeAdopter::moveNodeToNewDocument(Node& node, Document& oldDocument, Document& newDocument) const
131{
132    ASSERT(oldDocument != newDocument);
133
134    if (node.hasRareData()) {
135        NodeRareData* rareData = node.rareData();
136        if (rareData->nodeLists())
137            rareData->nodeLists()->adoptDocument(oldDocument, newDocument);
138    }
139
140    oldDocument.moveNodeIteratorsToNewDocument(node, newDocument);
141
142    if (node.isShadowRoot())
143        toShadowRoot(node).setDocument(newDocument);
144
145#if ENABLE(ASSERT)
146    didMoveToNewDocumentWasCalled = false;
147    oldDocumentDidMoveToNewDocumentWasCalledWith = &oldDocument;
148#endif
149
150    node.didMoveToNewDocument(oldDocument);
151    ASSERT(didMoveToNewDocumentWasCalled);
152}
153
154}
155