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