Change copyrights from Nokia to Digia
[profile/ivi/qtdeclarative.git] / src / qml / qml / v8 / qv8include.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qv8include_p.h"
43
44 #include <QtQml/qjsengine.h>
45 #include <QtNetwork/qnetworkrequest.h>
46 #include <QtNetwork/qnetworkreply.h>
47 #include <QtCore/qfile.h>
48 #include <QtQml/qqmlfile.h>
49
50 #include <private/qqmlengine_p.h>
51
52 QT_BEGIN_NAMESPACE
53
54 QV8Include::QV8Include(const QUrl &url, QV8Engine *engine, QQmlContextData *context,
55                        v8::Handle<v8::Object> qmlglobal, v8::Handle<v8::Function> callback)
56 : m_engine(engine), m_network(0), m_reply(0), m_url(url), m_redirectCount(0), m_context(context)
57 {
58     m_qmlglobal = qPersistentNew<v8::Object>(qmlglobal);
59     if (!callback.IsEmpty())
60         m_callbackFunction = qPersistentNew<v8::Function>(callback);
61
62     m_resultObject = qPersistentNew<v8::Object>(resultValue());
63
64     m_network = engine->networkAccessManager();
65
66     QNetworkRequest request;
67     request.setUrl(url);
68
69     m_reply = m_network->get(request);
70     QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished()));
71 }
72
73 QV8Include::~QV8Include()
74 {
75     delete m_reply; m_reply = 0;
76     qPersistentDispose(m_callbackFunction);
77     qPersistentDispose(m_resultObject);
78 }
79
80 v8::Local<v8::Object> QV8Include::resultValue(Status status)
81 {
82     // XXX It seems inefficient to create this object from scratch each time.
83     v8::Local<v8::Object> result = v8::Object::New();
84     result->Set(v8::String::New("OK"), v8::Integer::New(Ok));
85     result->Set(v8::String::New("LOADING"), v8::Integer::New(Loading));
86     result->Set(v8::String::New("NETWORK_ERROR"), v8::Integer::New(NetworkError));
87     result->Set(v8::String::New("EXCEPTION"), v8::Integer::New(Exception));
88
89     result->Set(v8::String::New("status"), v8::Integer::New(status));
90
91     return result;
92 }
93
94 void QV8Include::callback(QV8Engine *engine, v8::Handle<v8::Function> callback, v8::Handle<v8::Object> status)
95 {
96     if (!callback.IsEmpty()) {
97         v8::Handle<v8::Value> args[] = { status };
98         v8::TryCatch tc;
99         callback->Call(engine->global(), 1, args);
100     }
101 }
102
103 v8::Handle<v8::Object> QV8Include::result()
104 {
105     return m_resultObject;
106 }
107
108 #define INCLUDE_MAXIMUM_REDIRECT_RECURSION 15
109 void QV8Include::finished()
110 {
111     m_redirectCount++;
112
113     if (m_redirectCount < INCLUDE_MAXIMUM_REDIRECT_RECURSION) {
114         QVariant redirect = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
115         if (redirect.isValid()) {
116             m_url = m_url.resolved(redirect.toUrl());
117             delete m_reply; 
118             
119             QNetworkRequest request;
120             request.setUrl(m_url);
121
122             m_reply = m_network->get(request);
123             QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished()));
124             return;
125         }
126     }
127
128     v8::HandleScope handle_scope;
129
130     if (m_reply->error() == QNetworkReply::NoError) {
131         QByteArray data = m_reply->readAll();
132
133         QString code = QString::fromUtf8(data);
134         QQmlScript::Parser::extractPragmas(code);
135
136         QQmlContextData *importContext = new QQmlContextData;
137         importContext->isInternal = true;
138         importContext->isJSContext = true;
139         importContext->url = m_url;
140         importContext->isPragmaLibraryContext = m_context->isPragmaLibraryContext;
141         importContext->setParent(m_context, true);
142
143         v8::Context::Scope ctxtscope(m_engine->context());
144         v8::TryCatch try_catch;
145
146         v8::Local<v8::Script> script = m_engine->qmlModeCompile(code, m_url.toString());
147
148         if (!try_catch.HasCaught()) {
149             m_engine->contextWrapper()->addSubContext(m_qmlglobal, script, importContext);
150             script->Run(m_qmlglobal);
151         }
152
153         if (try_catch.HasCaught()) {
154             m_resultObject->Set(v8::String::New("status"), v8::Integer::New(Exception));
155             m_resultObject->Set(v8::String::New("exception"), try_catch.Exception());
156         } else {
157             m_resultObject->Set(v8::String::New("status"), v8::Integer::New(Ok));
158         }
159     } else {
160         m_resultObject->Set(v8::String::New("status"), v8::Integer::New(NetworkError));
161     }
162
163     callback(m_engine, m_callbackFunction, m_resultObject);
164
165     disconnect();
166     deleteLater();
167 }
168
169 /*
170     Documented in qv8engine.cpp
171 */
172 v8::Handle<v8::Value> QV8Include::include(const v8::Arguments &args)
173 {
174     if (args.Length() == 0)
175         return v8::Undefined();
176
177     QV8Engine *engine = V8ENGINE();
178     QQmlContextData *context = engine->callingContext();
179
180     if (!context || !context->isJSContext) 
181         V8THROW_ERROR("Qt.include(): Can only be called from JavaScript files");
182
183     QUrl url(context->resolvedUrl(QUrl(engine->toString(args[0]->ToString()))));
184     
185     v8::Local<v8::Function> callbackFunction;
186     if (args.Length() >= 2 && args[1]->IsFunction())
187         callbackFunction = v8::Local<v8::Function>::Cast(args[1]);
188
189     QString localFile = QQmlFile::urlToLocalFileOrQrc(url);
190
191     v8::Local<v8::Object> result;
192
193     if (localFile.isEmpty()) {
194
195         QV8Include *i = new QV8Include(url, engine, context, 
196                                        v8::Context::GetCallingQmlGlobal(), 
197                                        callbackFunction);
198         result = v8::Local<v8::Object>::New(i->result());
199
200     } else { 
201
202         QFile f(localFile);
203
204         if (f.open(QIODevice::ReadOnly)) {
205             QByteArray data = f.readAll();
206             QString code = QString::fromUtf8(data);
207             QQmlScript::Parser::extractPragmas(code);
208
209             QQmlContextData *importContext = new QQmlContextData;
210             importContext->isInternal = true;
211             importContext->isJSContext = true;
212             importContext->url = url;
213             importContext->setParent(context, true);
214
215             v8::TryCatch try_catch;
216
217             v8::Local<v8::Script> script = engine->qmlModeCompile(code, url.toString());
218
219             if (!try_catch.HasCaught()) {
220                 v8::Local<v8::Object> qmlglobal = v8::Context::GetCallingQmlGlobal();
221                 engine->contextWrapper()->addSubContext(qmlglobal, script, importContext);
222                 script->Run(qmlglobal);
223             }
224
225             if (try_catch.HasCaught()) {
226                 result = resultValue(Exception);
227                 result->Set(v8::String::New("exception"), try_catch.Exception());
228             } else {
229                 result = resultValue(Ok);
230             }
231
232         } else {
233             result = resultValue(NetworkError);
234         }
235
236         callback(engine, callbackFunction, result);
237     }
238
239     if (result.IsEmpty())
240         return v8::Undefined();
241     else 
242         return result;
243 }
244
245 QT_END_NAMESPACE