A REPL for PhantomJS

This covers [Issue 252](http://code.google.com/p/phantomjs/issues/detail?id=252)

The commit is composed of 12 squashed commits:

commit efdc6ba4f143c30a690fd97d92d80fa412e79999
Author: Ivan De Marino <ivan.de.marino@gmail.com>
Date:   Mon Feb 27 00:19:36 2012 +0000

    Pretty-pringing and Completion Caching done!

    * This completes pretty-printing for the result of evaluated
    * expressions in the REPL.
    * Also, now we cache the "possible completions", to speed things up
    * a bit (nothing fancy though).
    * Minor tweaks to the internal doc and the way we "mock"
    * pretty-printing for QObjects/REPLCompletanle
    * All tests passing :)

commit 1f9ef690e112a535b431fca409b77bb9c09d1c70
Author: Ivan De Marino <ivan.de.marino@gmail.com>
Date:   Sun Feb 26 22:35:00 2012 +0000

    Moving most of REPL shim JavaScritp code in a separate file. Way
easier to work on.

commit 02d460a16fee14e7096ae7d899c03902c5b8a9c6
Author: Ivan De Marino <ivan.de.marino@gmail.com>
Date:   Sat Feb 25 20:25:18 2012 +0000

    Initialisation of the Completions is now done in a pure virtual.

    This means that every REPLCompletable object will ACTUALLY register
completion strings, ONLY if we are running a REPL
    and that object is ACTUALLY created.
    Otherwise, why bother?

    Adding completions for all exposed REPLCompletable objects

    Also, fixed an issue with _getCompletions()

commit 412c3778fb04aa1c7379f8e760afce702b0428dd
Author: Ivan De Marino <ivan.de.marino@gmail.com>
Date:   Tue Feb 21 00:49:17 2012 +0000

    Few more tweaks to the REPL:

    - Now 'phantom' is the first QObject with proper completion
    - No repetition in QObject completions
    - LVAL of any user expression is now correctly prettified and
      printed

    Major things left to do:

    - Cache completions (using QCache?)
    - Add completions for the other QObject
    - When the LVAL of a user expression is a QObject, print what's
      expected, not the QObject "real" structure

commit 46f04713c8165d898055e15478bb31403f8c93f1
Author: Ivan De Marino <ivan.de.marino@gmail.com>
Date:   Tue Feb 7 10:13:23 2012 -0800

    Pretty-print expressions result

    Still not done though: there are issues with the NON-Native JS
objects.

commit 98b2fe67651dc750b62c6fa9cf1d80317fd9ae06
Author: Ivan De Marino <ivan.de.marino@gmail.com>
Date:   Fri Feb 3 00:22:52 2012 -0800

    Introducing REPLCompletable.

    This class should be inherited by any JavaScript-exposed QObject, to
ensure correct Auto-Completion.

    Correct auto-completion for QObjects.

    - Now even QObjects can correctly provide auto-completion, and avoid
      showing "not for users" methods
    - The strings used for the auto-completion are stored in a single
      Index: minimum memory footprint
    - Still, there is optimization that should be done (when "searching"
      for the right completion by prefix)
    - Completion for the objects not set up yet, but now it's just a
      trivial sequence of "addCompletion('bla')" in their constructors

commit 9bd48618154b1530a37b41f4060440184e23253d
Author: Ivan De Marino <ivan.de.marino@gmail.com>
Date:   Thu Feb 2 00:20:25 2012 -0800

    Changing the way we import Linenoise.

    Will just import a specific commit, and update manually when needed.

commit cfc9bae9fbdab13b01019b34b7cbd565e3153780
Author: Ivan De Marino <ivan.de.marino@gmail.com>
Date:   Sun Jan 29 23:22:26 2012 -0800

    Made the REPL into a Singleton. With Auto-completion!.

    Reasons:
    1) Needed a pointer to function (i.e. a static method) to be used
with Linenoise to provide auto-completions
    2) It makes more sense, as it's not like we are going to have 2 REPL
running at the same time, are we?

    There are problems to address:
    - the enumeration in JS seems to return only the native interface of
      our objects
    - the function completions contain argument types of those functions
    - "private" methods are exposed

commit c78bd32e17f8e0e4cc4a0066858de8cc81d33b97
Author: Ivan De Marino <ivan.de.marino@gmail.com>
Date:   Sun Jan 29 22:10:20 2012 -0800

    Migrating from the original, now [unmantained
Linenoise](https://github.com/antirez/linenoise) to the fairly active
[tadmarshall fork](https://github.com/tadmarshall/linenoise).

    Also now the project is imported as a Git Submodule.
    Having migrated to the latest Linenoise (see prev. commit), now this
_SHOULD_ work on Windows too.
    But, of course, this needs testing. :)

commit 43713c5723d7c5ed446ba41ae8d6f8c9feba7f9b
Author: Ivan De Marino <ivan.de.marino@gmail.com>
Date:   Tue Jan 24 23:17:06 2012 -0800

    Now that the basics work, I'm adding support for REPL history.

    This is something almost everyone today is accustomed to.
    Also, now REPL history works!
    And I found some useful resources to solve pending TODOs.

commit 31e5f88b044a5b4a823c67527ef8c245d2ac7863
Author: Ivan De Marino <ivan.de.marino@gmail.com>
Date:   Sun Jan 22 20:56:36 2012 -0800

    Adding Linenoise Project (https://github.com/antirez/linenoise).

    For now is included as a drop-in set of files.
    Later on, if the Linenoise project has frequent
    updates, we might prefer to do it as a
    git-submodule.

commit 4be9c15c65db4767e482fba0be13f8aab286d5f3
Author: Ivan De Marino <ivan.de.marino@gmail.com>
Date:   Thu Jan 5 15:31:13 2012 +0000

    First simple REPL implementation.

    - Not complete
    - Still doesn't handle arrow keys (needed for history)
1.5
Ivan De Marino 2012-01-05 15:31:13 +00:00 committed by Ariya Hidayat
parent a0ef8cb898
commit 61a3bf9021
24 changed files with 2558 additions and 87 deletions

View File

@ -38,7 +38,7 @@
// File
// public:
File::File(QFile *openfile, QTextCodec *codec, QObject *parent) :
QObject(parent),
REPLCompletable(parent),
m_file(openfile),
m_fileStream(0)
{
@ -180,11 +180,23 @@ void File::close()
deleteLater();
}
void File::initCompletions()
{
// Add completion for the Dynamic Properties of the 'file' object
// functions
addCompletion("read");
addCompletion("write");
addCompletion("readLine");
addCompletion("writeLine");
addCompletion("flush");
addCompletion("close");
}
// FileSystem
// public:
FileSystem::FileSystem(QObject *parent) :
QObject(parent)
FileSystem::FileSystem(QObject *parent)
: REPLCompletable(parent)
{ }
// public slots:
@ -424,3 +436,36 @@ bool FileSystem::_remove(const QString &path) const
bool FileSystem::_copy(const QString &source, const QString &destination) const {
return QFile(source).copy(destination);
}
void FileSystem::initCompletions()
{
// Add completion for the Dynamic Properties of the 'fs' object
// properties
addCompletion("separator");
addCompletion("workingDirectory");
// functions
addCompletion("list");
addCompletion("absolute");
addCompletion("exists");
addCompletion("isDirectory");
addCompletion("isFile");
addCompletion("isAbsolute");
addCompletion("isExecutable");
addCompletion("isReadable");
addCompletion("isWritable");
addCompletion("isLink");
addCompletion("changeWorkingDirectory");
addCompletion("makeDirectory");
addCompletion("makeTree");
addCompletion("removeDirectory");
addCompletion("removeTree");
addCompletion("copyTree");
addCompletion("open");
addCompletion("read");
addCompletion("write");
addCompletion("size");
addCompletion("remove");
addCompletion("copy");
addCompletion("move");
addCompletion("touch");
}

View File

@ -30,14 +30,15 @@
#ifndef FILESYSTEM_H
#define FILESYSTEM_H
#include <QObject>
#include <QStringList>
#include <QFile>
#include <QTextCodec>
#include <QTextStream>
#include <QVariant>
class File : public QObject
#include "replcompletable.h"
class File : public REPLCompletable
{
Q_OBJECT
@ -58,13 +59,16 @@ public slots:
void flush();
void close();
private:
virtual void initCompletions();
private:
QFile *m_file;
QTextStream *m_fileStream;
};
class FileSystem : public QObject
class FileSystem : public REPLCompletable
{
Q_OBJECT
Q_PROPERTY(QString workingDirectory READ workingDirectory)
@ -121,6 +125,9 @@ public slots:
bool isReadable(const QString &path) const;
bool isWritable(const QString &path) const;
bool isLink(const QString &path) const;
private:
virtual void initCompletions();
};
#endif // FILESYSTEM_H

19
src/linenoise/README.md Normal file
View File

@ -0,0 +1,19 @@
This project contains the **Linenoise project**, initially released
by [Salvatore Sanfilippo](https://github.com/antirez). Here we import a fork
by [Tad Marshall](https://github.com/tadmarshall) that lives at
[github.com/tadmarshall/linenoise](https://github.com/tadmarshall/linenoise).
The version of Linenoise included in PhantomJS refers to the commit:
commit 7946e2c2d08df11dca2b99c5db40360c3d3e9a80
Author: Alan T. DeKok <aland@freeradius.org>
Date: Wed Oct 26 15:56:52 2011 +0200
Added character callbacks again
Some files not needed for PhantomJS are removed.
Linenoise is licensed under the BSD-license.
Praise to all the developers that contribute to this nice little pearl.

View File

@ -0,0 +1,9 @@
VPATH += $$PWD/src
INCLUDEPATH += $$PWD/src
DEFINES += USE_UTF8
SOURCES += linenoise.c \
utf8.c
HEADERS += linenoise.h \
utf8.h

View File

@ -0,0 +1,51 @@
# Linenoise
A minimal, zero-config, BSD licensed, readline replacement.
News: linenoise now includes minimal completion support, thanks to Pieter Noordhuis (@pnoordhuis).
News: linenoise is now part of [Android](http://android.git.kernel.org/?p=platform/system/core.git;a=tree;f=liblinenoise;h=56450eaed7f783760e5e6a5993ef75cde2e29dea;hb=HEAD Android)!
News: Win32 port, many cleanups and fixes from https://github.com/msteveb/linenoise.
News: added minimal character callbacks, so that the application can catch characters entered by the user, from Alan DeKok.
## Can a line editing library be 20k lines of code?
Line editing with some support for history is a really important feature for command line utilities. Instead of retyping almost the same stuff again and again it's just much better to hit the up arrow and edit on syntax errors, or in order to try a slightly different command. But apparently code dealing with terminals is some sort of Black Magic: readline is 30k lines of code, libedit 20k. Is it reasonable to link small utilities to huge libraries just to get a minimal support for line editing?
So what usually happens is either:
* Large programs with configure scripts disabling line editing if readline is not present in the system, or not supporting it at all since readline is GPL licensed and libedit (the BSD clone) is not as known and available as readline is (Real world example of this problem: Tclsh).
* Smaller programs not using a configure script not supporting line editing at all (A problem we had with Redis-cli for instance).
The result is a pollution of binaries without line editing support.
So I spent more or less two hours doing a reality check resulting in this little library: is it *really* needed for a line editing library to be 20k lines of code? Apparently not, it is possibe to get a very small, zero configuration, trivial to embed library, that solves the problem. Smaller programs will just include this, supporting line editing out of the box. Larger programs may use this little library or just checking with configure if readline/libedit is available and resorting to linenoise if not.
## Terminals, in 2010.
Apparently almost every terminal you can happen to use today has some kind of support for VT100 alike escape sequences. So I tried to write a lib using just very basic VT100 features. The resulting library appears to work everywhere I tried to use it.
Since it's so young I guess there are a few bugs, or the lib may not compile or work with some operating system, but it's a matter of a few weeks and eventually we'll get it right, and there will be no excuses for not shipping command line tools without built-in line editing support.
The library started off at less than 400 lines of code, and is now almost 1500 lines of code. However, it now includes support for UTF-8 and Win32. In order to use it in your project just look at the *example.c* file in the source distribution, it is trivial. Linenoise is BSD code, so you can use both in free software and commercial software.
## Tested with...
* Linux text only console ($TERM = linux)
* Linux KDE terminal application ($TERM = xterm)
* Linux xterm ($TERM = xterm)
* Mac OS X iTerm ($TERM = xterm)
* Mac OS X default Terminal.app ($TERM = xterm)
* OpenBSD 4.5 through an OSX Terminal.app ($TERM = screen)
* IBM AIX 6.1
* FreeBSD xterm ($TERM = xterm)
Please test it everywhere you can and report back!
## Let's push this forward!
Please fork it and add something interesting and send me a pull request. What's especially interesting are fixes, new key bindings, completion.
Send feedbacks to antirez at gmail

View File

@ -0,0 +1,90 @@
#include <stdio.h>
#include <stdlib.h>
#include "linenoise.h"
#ifndef NO_COMPLETION
void completion(const char *buf, linenoiseCompletions *lc) {
if (buf[0] == 'h') {
linenoiseAddCompletion(lc,"hello");
linenoiseAddCompletion(lc,"hello there");
}
}
#endif
static int in_string = 0;
static size_t string_start = 0;
int foundspace(const char *buf, size_t len, char c) {
if (in_string) return 0;
if (len == 0) return 1;
if (buf[len -1] == c) return 1;
printf("\r\nSPACE!\r\n");
return 0;
}
int escapedquote(const char *start)
{
while (*start) {
if (*start == '\\') {
if (!start[1]) return 1;
start += 2;
}
start++;
}
return 0;
}
int foundquote(const char *buf, size_t len, char c) {
if (!in_string) {
in_string = 1;
string_start = len;
return 0;
}
if (buf[string_start] != c) return 0;
if (escapedquote(buf + string_start)) return 0;
in_string = 0;
printf("\r\nSTRING %s%c\r\n", buf + string_start, buf[string_start]);
string_start = 0;
return 0;
}
int foundhelp(const char *buf, size_t len, char c) {
if (in_string) return 0;
len = len; /* -Wunused */
c = c; /* -Wunused */
printf("?\r\nHELP: %s\r\n", buf);
return 1;
}
int main(void) {
char *line;
#ifndef NO_COMPLETION
linenoiseSetCompletionCallback(completion);
#endif
linenoiseHistoryLoad("history.txt"); /* Load the history at startup */
linenoiseSetCharacterCallback(foundspace, ' ');
linenoiseSetCharacterCallback(foundquote, '"');
linenoiseSetCharacterCallback(foundquote, '\'');
linenoiseSetCharacterCallback(foundhelp, '?');
while((line = linenoise("hello> ")) != NULL) {
if (line[0] != '\0') {
printf("echo: '%s'\n", line);
linenoiseHistoryAdd(line);
linenoiseHistorySave("history.txt"); /* Save every new entry */
}
free(line);
}
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,62 @@
/* linenoise.h -- guerrilla line editing library against the idea that a
* line editing lib needs to be 20,000 lines of C code.
*
* See linenoise.c for more information.
*
* ------------------------------------------------------------------------
*
* Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __LINENOISE_H
#define __LINENOISE_H
#ifndef NO_COMPLETION
typedef struct linenoiseCompletions {
size_t len;
char **cvec;
} linenoiseCompletions;
typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
#endif
typedef int(linenoiseCharacterCallback)(const char *, size_t, char);
void linenoiseSetCharacterCallback(linenoiseCharacterCallback *, char);
char *linenoise(const char *prompt);
int linenoiseHistoryAdd(const char *line);
int linenoiseHistorySetMaxLen(int len);
int linenoiseHistorySave(const char *filename);
int linenoiseHistoryLoad(const char *filename);
void linenoiseHistoryFree(void);
char **linenoiseHistory(int *len);
#endif /* __LINENOISE_H */

115
src/linenoise/src/utf8.c Normal file
View File

@ -0,0 +1,115 @@
/**
* UTF-8 utility functions
*
* (c) 2010 Steve Bennett <steveb@workware.net.au>
*
* See LICENCE for licence details.
*/
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "utf8.h"
#ifdef USE_UTF8
int utf8_fromunicode(char *p, unsigned short uc)
{
if (uc <= 0x7f) {
*p = uc;
return 1;
}
else if (uc <= 0x7ff) {
*p++ = 0xc0 | ((uc & 0x7c0) >> 6);
*p = 0x80 | (uc & 0x3f);
return 2;
}
else {
*p++ = 0xe0 | ((uc & 0xf000) >> 12);
*p++ = 0x80 | ((uc & 0xfc0) >> 6);
*p = 0x80 | (uc & 0x3f);
return 3;
}
}
int utf8_charlen(int c)
{
if ((c & 0x80) == 0) {
return 1;
}
if ((c & 0xe0) == 0xc0) {
return 2;
}
if ((c & 0xf0) == 0xe0) {
return 3;
}
if ((c & 0xf8) == 0xf0) {
return 4;
}
/* Invalid sequence */
return -1;
}
int utf8_strlen(const char *str, int bytelen)
{
int charlen = 0;
if (bytelen < 0) {
bytelen = strlen(str);
}
while (bytelen) {
int c;
int l = utf8_tounicode(str, &c);
charlen++;
str += l;
bytelen -= l;
}
return charlen;
}
int utf8_index(const char *str, int index)
{
const char *s = str;
while (index--) {
int c;
s += utf8_tounicode(s, &c);
}
return s - str;
}
int utf8_charequal(const char *s1, const char *s2)
{
int c1, c2;
utf8_tounicode(s1, &c1);
utf8_tounicode(s2, &c2);
return c1 == c2;
}
int utf8_tounicode(const char *str, int *uc)
{
unsigned const char *s = (unsigned const char *)str;
if (s[0] < 0xc0) {
*uc = s[0];
return 1;
}
if (s[0] < 0xe0) {
if ((s[1] & 0xc0) == 0x80) {
*uc = ((s[0] & ~0xc0) << 6) | (s[1] & ~0x80);
return 2;
}
}
else if (s[0] < 0xf0) {
if (((str[1] & 0xc0) == 0x80) && ((str[2] & 0xc0) == 0x80)) {
*uc = ((s[0] & ~0xe0) << 12) | ((s[1] & ~0x80) << 6) | (s[2] & ~0x80);
return 3;
}
}
/* Invalid sequence, so just return the byte */
*uc = *s;
return 1;
}
#endif

79
src/linenoise/src/utf8.h Normal file
View File

@ -0,0 +1,79 @@
#ifndef UTF8_UTIL_H
#define UTF8_UTIL_H
/**
* UTF-8 utility functions
*
* (c) 2010 Steve Bennett <steveb@workware.net.au>
*
* See LICENCE for licence details.
*/
#ifndef USE_UTF8
#include <ctype.h>
/* No utf-8 support. 1 byte = 1 char */
#define utf8_strlen(S, B) ((B) < 0 ? (int)strlen(S) : (B))
#define utf8_tounicode(S, CP) (*(CP) = (unsigned char)*(S), 1)
#define utf8_index(C, I) (I)
#define utf8_charlen(C) 1
#else
/**
* Converts the given unicode codepoint (0 - 0xffff) to utf-8
* and stores the result at 'p'.
*
* Returns the number of utf-8 characters (1-3).
*/
int utf8_fromunicode(char *p, unsigned short uc);
/**
* Returns the length of the utf-8 sequence starting with 'c'.
*
* Returns 1-4, or -1 if this is not a valid start byte.
*
* Note that charlen=4 is not supported by the rest of the API.
*/
int utf8_charlen(int c);
/**
* Returns the number of characters in the utf-8
* string of the given byte length.
*
* Any bytes which are not part of an valid utf-8
* sequence are treated as individual characters.
*
* The string *must* be null terminated.
*
* Does not support unicode code points > \uffff
*/
int utf8_strlen(const char *str, int bytelen);
/**
* Returns the byte index of the given character in the utf-8 string.
*
* The string *must* be null terminated.
*
* This will return the byte length of a utf-8 string
* if given the char length.
*/
int utf8_index(const char *str, int charindex);
/**
* Returns the unicode codepoint corresponding to the
* utf-8 sequence 'str'.
*
* Stores the result in *uc and returns the number of bytes
* consumed.
*
* If 'str' is null terminated, then an invalid utf-8 sequence
* at the end of the string will be returned as individual bytes.
*
* If it is not null terminated, the length *must* be checked first.
*
* Does not support unicode code points > \uffff
*/
int utf8_tounicode(const char *str, int *uc);
#endif
#endif

View File

@ -40,12 +40,6 @@ int main(int argc, char** argv)
// Registering an alternative Message Handler
qInstallMsgHandler(Utils::messageHandler);
// Check number of parameters passed
if (argc < 2) {
Utils::showUsage();
return 1;
}
QApplication app(argc, argv);
app.setWindowIcon(QIcon(":/phantomjs-icon.png"));

View File

@ -41,11 +41,12 @@
#include "utils.h"
#include "webpage.h"
#include "webserver.h"
#include "repl.h"
// public:
Phantom::Phantom(QObject *parent)
: QObject(parent)
: REPLCompletable(parent)
, m_terminated(false)
, m_returnValue(0)
, m_filesystem(0)
@ -79,11 +80,6 @@ Phantom::Phantom(QObject *parent)
m_page = new WebPage(this, &m_config, QUrl::fromLocalFile(m_config.scriptFile()));
m_pages.append(m_page);
if (m_config.scriptFile().isEmpty()) {
Utils::showUsage();
return;
}
if (m_config.proxyHost().isEmpty()) {
QNetworkProxyFactory::setUseSystemConfiguration(true);
} else {
@ -126,7 +122,6 @@ Phantom::~Phantom()
{
}
QStringList Phantom::args() const
{
return m_config.scriptArgs();
@ -152,20 +147,24 @@ bool Phantom::execute()
if (m_terminated)
return false;
if (m_config.scriptFile().isEmpty())
return false;
if (m_config.debug())
{
if (!Utils::loadJSForDebug(m_config.scriptFile(), m_scriptFileEnc, QDir::currentPath(), m_page->mainFrame(), m_config.remoteDebugAutorun())) {
m_returnValue = -1;
return false;
}
m_page->showInspector(m_config.remoteDebugPort());
if (m_config.scriptFile().isEmpty()) {
// REPL mode requested
// Create the REPL: it will launch itself, no need to store this variable.
REPL::getInstance(m_page->mainFrame(), this);
} else {
if (!Utils::injectJsInFrame(m_config.scriptFile(), m_scriptFileEnc, QDir::currentPath(), m_page->mainFrame(), true)) {
m_returnValue = -1;
return false;
// Load the User Script
if (m_config.debug()) {
// Debug enabled
if (!Utils::loadJSForDebug(m_config.scriptFile(), m_scriptFileEnc, QDir::currentPath(), m_page->mainFrame(), m_config.remoteDebugAutorun())) {
m_returnValue = -1;
return false;
}
m_page->showInspector(m_config.remoteDebugPort());
} else {
if (!Utils::injectJsInFrame(m_config.scriptFile(), m_scriptFileEnc, QDir::currentPath(), m_page->mainFrame(), true)) {
m_returnValue = -1;
return false;
}
}
}
@ -251,9 +250,9 @@ bool Phantom::injectJs(const QString &jsFilePath)
void Phantom::exit(int code)
{
if (m_config.debug())
if (m_config.debug()) {
Terminal::instance()->cout("Phantom::exit() called but not quitting in debug mode.");
else {
} else {
doExit(code);
}
}
@ -263,34 +262,6 @@ void Phantom::debugExit(int code)
doExit(code);
}
void Phantom::doExit(int code)
{
if (m_config.debug())
{
Utils::cleanupFromDebug();
}
m_terminated = true;
m_returnValue = code;
qDeleteAll(m_pages);
m_pages.clear();
m_page = 0;
QApplication::instance()->exit(code);
}
void
Phantom::onInitialized()
{
// Add 'phantom' object to the global scope
m_page->mainFrame()->addToJavaScriptWindowObject("phantom", this);
// Bootstrap the PhantomJS scope
m_page->mainFrame()->evaluateJavaScript(Utils::readResourceFileUtf8(":/bootstrap.js"));
}
// private slots:
void Phantom::printConsoleMessage(const QString &message, int lineNumber, const QString &source)
{
@ -299,3 +270,44 @@ void Phantom::printConsoleMessage(const QString &message, int lineNumber, const
msg = source + ":" + QString::number(lineNumber) + " " + msg;
Terminal::instance()->cout(msg);
}
void Phantom::onInitialized()
{
// Add 'phantom' object to the global scope
m_page->mainFrame()->addToJavaScriptWindowObject("phantom", this);
// Bootstrap the PhantomJS scope
m_page->mainFrame()->evaluateJavaScript(Utils::readResourceFileUtf8(":/bootstrap.js"));
}
// private:
void Phantom::doExit(int code)
{
if (m_config.debug())
{
Utils::cleanupFromDebug();
}
emit aboutToExit(code);
m_terminated = true;
m_returnValue = code;
qDeleteAll(m_pages);
m_pages.clear();
m_page = 0;
QApplication::instance()->exit(code);
}
void Phantom::initCompletions()
{
// Add completion for the Dynamic Properties of the 'phantom' object
// properties
addCompletion("args");
addCompletion("defaultPageSettings");
addCompletion("libraryPath");
addCompletion("outputEncoding");
addCompletion("scriptName");
addCompletion("version");
// functions
addCompletion("exit");
addCompletion("injectJs");
}

View File

@ -33,14 +33,16 @@
#include <QtGui>
class WebPage;
class WebServer;
#include "csconverter.h"
#include "filesystem.h"
#include "encoding.h"
#include "config.h"
#include "replcompletable.h"
class Phantom: public QObject
class WebPage;
class WebServer;
class Phantom: public REPLCompletable
{
Q_OBJECT
Q_PROPERTY(QStringList args READ args)
@ -82,12 +84,16 @@ public slots:
void exit(int code = 0);
void debugExit(int code = 0);
signals:
void aboutToExit(int code);
private slots:
void printConsoleMessage(const QString &msg, int lineNumber, const QString &source);
void onInitialized();
private:
void doExit(int code);
virtual void initCompletions();
Encoding m_scriptFileEnc;
WebPage *m_page;

View File

@ -21,7 +21,9 @@ HEADERS += csconverter.h \
filesystem.h \
terminal.h \
encoding.h \
config.h
config.h \
repl.h \
replcompletable.h
SOURCES += phantom.cpp \
webpage.cpp \
@ -34,17 +36,21 @@ SOURCES += phantom.cpp \
filesystem.cpp \
terminal.cpp \
encoding.cpp \
config.cpp
config.cpp \
repl.cpp \
replcompletable.cpp
OTHER_FILES += usage.txt \
bootstrap.js \
configurator.js \
modules/fs.js \
modules/webpage.js \
modules/webserver.js
modules/webserver.js \
repl.js
include(gif/gif.pri)
include(mongoose/mongoose.pri)
include(linenoise/linenoise.pri)
win32: RC_FILE = phantomjs_win.rc
os2: RC_FILE = phantomjs_os2.rc

View File

@ -9,5 +9,6 @@
<file>modules/webpage.js</file>
<file>modules/webserver.js</file>
<file>modules/fs.js</file>
<file>repl.js</file>
</qresource>
</RCC>

180
src/repl.cpp Normal file
View File

@ -0,0 +1,180 @@
/*
This file is part of the PhantomJS project from Ofi Labs.
Copyright (C) 2011 Ivan De Marino <ivan.de.marino@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "repl.h"
#include <QDesktopServices>
#include <QTimer>
#include <QDir>
#include <QRegExp>
#include "consts.h"
#include "terminal.h"
#include "utils.h"
#define PROMPT "phantomjs> "
#define HISTORY_FILENAME "phantom_repl_history"
// Only with word characters, spaces and the dot ('.')
// we can still attempt to offer a completion to the user
#define REGEXP_NON_COMPLETABLE_CHARS "[^\\w\\s\\.]"
// JS Code to find possible completions
#define JS_RETURN_POSSIBLE_COMPLETIONS "REPL._getCompletions(%1, \"%2\");"
// JS Code to evaluate User Input and prettify the expression result
#define JS_EVAL_USER_INPUT \
"try { " \
"REPL._lastEval = eval(\"%1\");" \
"console.log(JSON.stringify(REPL._lastEval, REPL._expResStringifyReplacer, ' ')); " \
"} catch(e) { " \
"if (e instanceof TypeError) { " \
"console.error(\"'%1' is a cyclic structure\"); " \
"} else { " \
"console.error(e.message);" \
"}" \
"} "
// public:
bool REPL::instanceExists()
{
return REPL::getInstance() != NULL;
}
REPL *REPL::getInstance(QWebFrame *webframe, Phantom *parent)
{
static REPL *singleton = NULL;
if (!singleton && webframe && parent) {
// This will create the singleton only when all the parameters are given
singleton = new REPL(webframe, parent);
}
return singleton;
}
// private:
REPL::REPL(QWebFrame *webframe, Phantom *parent)
: QObject(parent),
m_looping(true)
{
m_webframe = webframe;
m_parentPhantom = parent;
m_historyFilepath = QString("%1/%2").arg(
QDesktopServices::storageLocation(QDesktopServices::DataLocation),
HISTORY_FILENAME).toLocal8Bit();
// Ensure the location for the history file exists
QDir().mkpath(QDesktopServices::storageLocation(QDesktopServices::DataLocation));
// Listen for Phantom exit(ing)
connect(m_parentPhantom, SIGNAL(aboutToExit(int)), this, SLOT(stopLoop(int)));
// Set the static callback to offer Completions to the User
linenoiseSetCompletionCallback(REPL::offerCompletion);
// Inject REPL utility functions
m_webframe->evaluateJavaScript(Utils::readResourceFileUtf8(":/repl.js"));
// Start the REPL's loop
QTimer::singleShot(0, this, SLOT(startLoop()));
}
void REPL::offerCompletion(const char *buf, linenoiseCompletions *lc)
{
// IF there is a ( or ), then do nothing (we can't complete)
QString buffer(buf);
int lastIndexOfDot = -1;
QString toInspect, toComplete;
QRegExp nonCompletableChars(REGEXP_NON_COMPLETABLE_CHARS);
// If we encounter a non acceptable character (see above)
if (buffer.contains(nonCompletableChars)) {
return;
}
// Decompose what user typed so far in 2 parts: what toInspect and what toComplete.
lastIndexOfDot = buffer.lastIndexOf('.');
if (lastIndexOfDot > -1) {
toInspect = buffer.left(lastIndexOfDot);
toComplete = buffer.right(buffer.length() - lastIndexOfDot -1);
} else {
// Nothing to inspect: use the global "window" object
toInspect = "window";
toComplete = buffer;
}
// This will return an array of String with the possible completions
QStringList completions = REPL::getInstance()->m_webframe->evaluateJavaScript(
QString(JS_RETURN_POSSIBLE_COMPLETIONS).arg(
toInspect,
toComplete)
).toStringList();
foreach (QString c, completions) {
if (lastIndexOfDot > -1) {
// Preserve the "toInspect" portion of the string to complete
linenoiseAddCompletion(lc, QString("%1.%2").arg(toInspect, c).toLocal8Bit().data());
} else {
linenoiseAddCompletion(lc, c.toLocal8Bit().data());
}
}
}
// private slots:
void REPL::startLoop()
{
char *userInput;
// Load REPL history
linenoiseHistoryLoad(m_historyFilepath.data()); //< requires "char *"
while(m_looping && (userInput = linenoise(PROMPT)) != NULL) {
if (userInput[0] != '\0') {
// Send the user input to the main Phantom frame for evaluation
m_webframe->evaluateJavaScript(
QString(JS_EVAL_USER_INPUT).arg(
QString(userInput).replace('"', "\\\"")));
// Save command in the REPL history
linenoiseHistoryAdd(userInput);
linenoiseHistorySave(m_historyFilepath.data()); //< requires "char *"
}
free(userInput);
}
// If still "looping", close Phantom (usually caused by "CTRL+C" / "CTRL+D")
if (m_looping) {
m_parentPhantom->exit();
}
}
void REPL::stopLoop(const int code)
{
Q_UNUSED(code);
m_looping = false;
}

76
src/repl.h Normal file
View File

@ -0,0 +1,76 @@
/*
This file is part of the PhantomJS project from Ofi Labs.
Copyright (C) 2011 Ivan De Marino <ivan.de.marino@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef REPL_H
#define REPL_H
#include <QWebFrame>
#include "phantom.h"
// Linenoise is a C Library: we need to externalise it's symbols for linkage
extern "C" {
#include "linenoise.h"
}
/**
* REPL. ReadEvalPrint Loop.
*
* This class realises the REPL functionality within PhantomJS.
* It's a Singleton: invoke "REPL::getInstance(QWebFrame *, Phantom *) to
* create the first-and-only instance, or no parameter to get the singleton
* if previously created.
*
* It's based the Linenoise library (https://github.com/tadmarshall/linenoise).
* More info about REPL: http://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop
*/
class REPL: public QObject
{
Q_OBJECT
public:
static bool instanceExists();
static REPL *getInstance(QWebFrame *webframe = NULL, Phantom *parent = NULL);
private:
REPL(QWebFrame *webframe, Phantom *parent);
static void offerCompletion(const char *buf, linenoiseCompletions *lc);
private slots:
void startLoop();
void stopLoop(const int code);
private:
QWebFrame *m_webframe;
Phantom *m_parentPhantom;
bool m_looping;
QByteArray m_historyFilepath;
};
#endif // REPL_H

98
src/repl.js Normal file
View File

@ -0,0 +1,98 @@
/*jslint sloppy: true, nomen: true */
/*
This file is part of the PhantomJS project from Ofi Labs.
Copyright (C) 2011 Ivan De Marino <ivan.de.marino@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
var REPL = REPL || {};
/**
* Return the Completions of the Object, applying the prefix
*
* @param obj Object to get completions of
* @param prefix Limit completions to the one starting with this prefix
*/
REPL._getCompletions = function (obj, prefix) {
var completions = [];
// If the given object is "(REPL)Completable", just return it's completions
if (obj._isCompletable && obj._isCompletable() === true) {
completions = obj._getCompletions(prefix || "");
} else {
// It's a JS Native Object: build the list of completions manually
for (k in obj) {
if (obj.hasOwnProperty(k) && k.indexOf(prefix || "") === 0) {
completions.push(k);
}
}
}
return completions;
};
/**
* This utility function is used to pretty-print the result of an expression.
* @see https://developer.mozilla.org/En/Using_native_JSON#The_replacer_parameter
*
* @param k Property key name - empty string if it's the object being stringified
* @param v Property value
*/
REPL._expResStringifyReplacer = function (k, v) {
var i, iarr,
mock = {},
funcToStr = "[Function]";
// If the result of the last evaluated expression is a REPLCompletable object
if (k === "" //< only first level of recursive calls
&& REPL._lastEval
&& REPL._lastEval._isCompletable
&& REPL._lastEval._isCompletable() === true) {
// Get all the completions for the REPLCompletable object we are going to pretty-print
iarr = REPL._lastEval._getCompletions("");
for (i in iarr) {
if (typeof(v[iarr[i]]) !== "undefined") {
// add a reference to this "real" property into the mock object
mock[iarr[i]] = v[iarr[i]];
} else {
// add a "function" for this "shimmed" property into the mock object
mock[iarr[i]] = funcToStr;
}
}
return mock;
}
// Else, just act normally
if (typeof(v) === "function") {
// Normally functions are ignored by JSON.stringify
return funcToStr;
}
return v;
};

86
src/replcompletable.cpp Normal file
View File

@ -0,0 +1,86 @@
#include "replcompletable.h"
// public:
REPLCompletable::REPLCompletable(QObject *parent)
: QObject(parent),
mCompletionsInitialised(false)
{ }
REPLCompletable::~REPLCompletable()
{ }
bool REPLCompletable::_isCompletable()
{
return true;
}
QStringList REPLCompletable::_getCompletions(const QString &prefix)
{
// First time this method is invoked, initialise the completions (lazy init)
if (!mCompletionsInitialised) {
initCompletions();
mCompletionsInitialised = true;
}
// If no prefix provided?
if (prefix.isEmpty()) {
// Return all the possible completions
return REPLCompletable::getCompletionsIndex()->values(
this->metaObject()->className());
}
// make a key to store the (new) completions list
QString cacheKey = QString("%1-%2").arg(this->metaObject()->className()).arg(prefix);
// If a list of completion withi this key is not already in the cache
if (!getCompletionsCache()->contains(cacheKey)) {
// Loop over the completions and pick the one that match the given prefix
QStringList allCompletions = REPLCompletable::getCompletionsIndex()->values(
this->metaObject()->className());
QStringList *matchingPrefixCompletions = new QStringList();
QStringList::iterator i;
for (i = allCompletions.begin(); i != allCompletions.end(); ++i) {
if (((QString) *i).startsWith(prefix)) {
matchingPrefixCompletions->append((QString) *i);
}
}
// Store the result in the cache
getCompletionsCache()->insert(cacheKey, matchingPrefixCompletions);
}
return *(getCompletionsCache()->object(cacheKey));
}
// protected:
void REPLCompletable::addCompletion(const char *completion)
{
addCompletion(QString(completion));
}
void REPLCompletable::addCompletion(QString completion)
{
// Accept a completion only if it's unique per MetaObject ClassName
if (!REPLCompletable::getCompletionsIndex()->contains(this->metaObject()->className(), completion)) {
REPLCompletable::getCompletionsIndex()->insert(this->metaObject()->className(), completion);
}
}
// private:
QMultiHash<const char *, QString> *REPLCompletable::getCompletionsIndex()
{
static QMultiHash<const char *, QString> *compIndex = NULL;
if (!compIndex) {
compIndex = new QMultiHash<const char *, QString>();
}
return compIndex;
}
QCache<QString, QStringList> *REPLCompletable::getCompletionsCache()
{
static QCache<QString, QStringList> *compCache = NULL;
if (!compCache) {
compCache = new QCache<QString, QStringList>();
}
return compCache;
}

60
src/replcompletable.h Normal file
View File

@ -0,0 +1,60 @@
#ifndef REPLCOMPLETABLE_H
#define REPLCOMPLETABLE_H
#include <QMultiHash>
#include <QStringList>
#include <QCache>
/**
* This subclass of QObject is used by the REPL to better control
* what is "shown" of a QObject exposed in the Javascript Runtime.
*
* By default the JS environment will see all the slots and the Q_INVOKABLE
* of a "exposed" QObject. But also some extra QObject specific methods
* that, in our case, we prefer not to list during REPL autocompletion
* listing or expression result prettyfication.
*/
class REPLCompletable : public QObject
{
Q_OBJECT
public:
REPLCompletable(QObject *parent = 0);
virtual ~REPLCompletable();
Q_INVOKABLE bool _isCompletable();
Q_INVOKABLE QStringList _getCompletions(const QString &prefixToComplete);
protected:
/**
* Used by sublcasses to register a possible "completion".
*
* @param completion Array of characters representing a function/property
* that will be listed as possible completion
*/
void addCompletion(const char *completion);
/**
* Used by sublcasses to register a possible "completion".
*
* @param completion String representing a function/property
* that will be listed as possible completion
*/
void addCompletion(QString completion);
private:
/**
* This is where subclasses should use REPLCompletable#addCompletion(...)
* to declare/register their completions for the REPL.
* This ensures that ONLY if a REPL is actually requested by the user,
* we bother registering the completion strings.
*/
virtual void initCompletions() = 0;
static QMultiHash<const char *, QString> *getCompletionsIndex();
static QCache<QString, QStringList> *getCompletionsCache();
private:
bool mCompletionsInitialised;
};
#endif // REPLCOMPLETABLE_H

View File

@ -58,7 +58,8 @@
class CustomPage: public QWebPage
{
Q_OBJECT
Q_OBJECT
public:
CustomPage(WebPage *parent = 0)
: QWebPage(parent)
@ -103,8 +104,9 @@ private:
friend class WebPage;
};
WebPage::WebPage(QObject *parent, const Config *config, const QUrl &baseUrl)
: QObject(parent)
: REPLCompletable(parent)
{
setObjectName("WebPage");
m_webPage = new CustomPage(this);
@ -631,4 +633,32 @@ void WebPage::sendEvent(const QString &type, const QVariant &arg1, const QVarian
}
}
void WebPage::initCompletions()
{
// Add completion for the Dynamic Properties of the 'webpage' object
// properties
addCompletion("clipRect");
addCompletion("content");
addCompletion("libraryPath");
addCompletion("settings");
addCompletion("viewportSize");
// functions
addCompletion("evaluate");
addCompletion("includeJs");
addCompletion("injectJs");
addCompletion("open");
addCompletion("release");
addCompletion("render");
addCompletion("sendEvent");
addCompletion("uploadFile");
// callbacks
addCompletion("onAlert");
addCompletion("onConsoleMessage");
addCompletion("onInitialized");
addCompletion("onLoadStarted");
addCompletion("onLoadFinished");
addCompletion("onResourceRequested");
addCompletion("onResourceReceived");
}
#include "webpage.moc"

View File

@ -35,13 +35,15 @@
#include <QVariantMap>
#include <QWebPage>
#include "replcompletable.h"
class Config;
class CustomPage;
class NetworkAccessManager;
class QWebInspector;
class Phantom;
class WebPage: public QObject
class WebPage: public REPLCompletable
{
Q_OBJECT
Q_PROPERTY(QString content READ content WRITE setContent)
@ -106,6 +108,17 @@ signals:
private slots:
void finish(bool ok);
private:
QImage renderImage();
bool renderPdf(const QString &fileName);
void applySettings(const QVariantMap &defaultSettings);
QString userAgent() const;
void emitAlert(const QString &msg);
void emitConsoleMessage(const QString &msg, int lineNumber, const QString &source);
virtual void initCompletions();
private:
CustomPage *m_webPage;
NetworkAccessManager *m_networkAccessManager;
@ -116,14 +129,6 @@ private:
QString m_libraryPath;
QWebInspector* m_inspector;
QImage renderImage();
bool renderPdf(const QString &fileName);
void applySettings(const QVariantMap &defaultSettings);
QString userAgent() const;
void emitAlert(const QString &msg);
void emitConsoleMessage(const QString &msg, int lineNumber, const QString &source);
friend class Phantom;
friend class CustomPage;

View File

@ -63,7 +63,7 @@ static void *callback(mg_event event,
}
WebServer::WebServer(QObject *parent, Config *config)
: QObject(parent)
: REPLCompletable(parent)
, m_config(config)
, m_ctx(0)
{
@ -216,11 +216,23 @@ void WebServer::handleRequest(mg_event event, mg_connection *conn, const mg_requ
*handled = false;
}
void WebServer::initCompletions()
{
// Add completion for the Dynamic Properties of the 'webpage' object
// properties
addCompletion("clipRect");
// functions
addCompletion("listen");
addCompletion("close");
// callbacks
addCompletion("onNewRequest");
}
//BEGIN WebServerResponse
WebServerResponse::WebServerResponse(mg_connection *conn)
: QObject()
: REPLCompletable()
, m_conn(conn)
, m_statusCode(200)
, m_headersSent(false)
@ -389,4 +401,15 @@ void WebServerResponse::setHeaders(const QVariantMap &headers)
m_headers = headers;
}
void WebServerResponse::initCompletions()
{
// Add completion for the Dynamic Properties of the 'webpage' object
// properties
addCompletion("statusCode");
addCompletion("headers");
// functions
addCompletion("writeHead");
addCompletion("write");
}
//END WebServerResponse

View File

@ -31,12 +31,12 @@
#ifndef WEBSERVER_H
#define WEBSERVER_H
#include <QObject>
#include <QVariantMap>
///TODO: is this ok, or should it be put into .cpp
/// can be done by introducing a WebServerPrivate *d;
#include "mongoose.h"
#include "replcompletable.h"
class Config;
@ -47,10 +47,10 @@ class WebServerResponse;
*
* see also: modules/webserver.js
*/
class WebServer : public QObject
class WebServer : public REPLCompletable
{
Q_OBJECT
Q_PROPERTY(QString port READ port);
Q_PROPERTY(QString port READ port)
public:
WebServer(QObject *parent, Config *config);
@ -85,6 +85,9 @@ private slots:
void handleRequest(mg_event event, mg_connection* conn, const mg_request_info* request,
bool* handled);
private:
virtual void initCompletions();
private:
Config *m_config;
mg_context *m_ctx;
@ -95,12 +98,12 @@ private:
/**
* Outgoing HTTP response to client.
*/
class WebServerResponse : public QObject
class WebServerResponse : public REPLCompletable
{
Q_OBJECT
Q_PROPERTY(int statusCode READ statusCode WRITE setStatusCode);
Q_PROPERTY(QVariantMap headers READ headers WRITE setHeaders);
Q_PROPERTY(int statusCode READ statusCode WRITE setStatusCode)
Q_PROPERTY(QVariantMap headers READ headers WRITE setHeaders)
public:
WebServerResponse(mg_connection *conn);
@ -135,6 +138,9 @@ public slots:
/// set all headers
void setHeaders(const QVariantMap &headers);
private:
virtual void initCompletions();
private:
mg_connection *m_conn;
int m_statusCode;