Browse Source

Merge branch 'master' of https://gitgud.io/odilitime/netrunner

pull/2/head
Odilitime 5 years ago
parent
commit
57b8807c64
  1. 2
      .gitignore
  2. 9
      README.md
  3. 2
      cr
  4. BIN
      reltools/.DS_Store
  5. 33
      src/BrowsingHistory.cpp
  6. 22
      src/BrowsingHistory.h
  7. 41
      src/CommandLineParams.cpp
  8. 17
      src/CommandLineParams.h
  9. 80
      src/Log.cpp
  10. 53
      src/Log.h
  11. 64
      src/StringUtils.cpp
  12. 5
      src/StringUtils.h
  13. 267
      src/URL.cpp
  14. 40
      src/URL.h
  15. 72
      src/WebResource.cpp
  16. 7
      src/WebResource.h
  17. 4
      src/graphics/components/AnimeComponent.cpp
  18. 1
      src/graphics/components/Component.cpp
  19. 2
      src/graphics/components/Component.h
  20. 5
      src/graphics/elements/AElement.cpp
  21. 89
      src/graphics/opengl/Window.cpp
  22. 10
      src/graphics/opengl/Window.h
  23. 13
      src/html/Node.cpp
  24. 2
      src/html/Node.h
  25. 18
      src/html/TagNode.cpp
  26. 2
      src/html/TagNode.h
  27. 2
      src/html/TextNode.h
  28. 154
      src/main.cpp
  29. 34
      src/networking/HTTPRequest.cpp
  30. 6
      src/networking/HTTPRequest.h
  31. 2
      src/networking/HTTPResponse.cpp
  32. 222
      src/networking/URI.cpp
  33. 23
      src/networking/URI.h
  34. 28
      visualc/NetRunner.sln
  35. BIN
      xcode/.DS_Store
  36. 168
      xcode/Saltasaurus.xcodeproj/project.pbxproj
  37. BIN
      xcode/Saltasaurus.xcodeproj/project.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate
  38. 33
      xcode/Saltasaurus.xcodeproj/xcuserdata/admin.xcuserdatad/xcschemes/Saltasaurus.xcscheme

2
.gitignore vendored

@ -4,3 +4,5 @@ gen @@ -4,3 +4,5 @@ gen
src/graphics/opengl/shaders/gen
netrunner
*.iml
tags
src/networking/URI

9
README.md

@ -32,15 +32,22 @@ I'd like to develop text-based structures for communication between each piece. @@ -32,15 +32,22 @@ I'd like to develop text-based structures for communication between each piece.
#### Void
`sudo xbps-install -S glew glfw harfbuzz-devel`
#### Gentoo
`sudo emerge freetype harfbuzz glew glfw`
## Binaries
### Linux
[2017-07-27 binary package](https://my.mixtape.moe/ltuspw.tar.gz)
[2017-07-31 binary package](https://my.mixtape.moe/imatcb.tar.gz)
GyroNinja.net is temporarily offline. Working with registrar to bring it back online.
[nightly binary only (no font files)](https://gyroninja.net:1615/job/NetRunner/lastSuccessfulBuild/artifact/netrunner)
### OSX
[2017-07-31 binary package](https://my.mixtape.moe/ywjanx.zip)
### Windows
[2017-07-31 binary package](https://my.mixtape.moe/hkpcyu.zip)
## Milestones
- Browse 4chan /g/ board

2
cr

@ -2,4 +2,4 @@ @@ -2,4 +2,4 @@
physicalCpuCount=$([[ $(uname) = 'Darwin' ]] &&
sysctl -n hw.physicalcpu_max ||
lscpu -p | egrep -v '^#' | sort -u -t, -k 2,4 | wc -l)
make -j$physicalCpuCount && ./netrunner http://motherfuckingwebsite.com/
make -j$physicalCpuCount && ./netrunner http://motherfuckingwebsite.com/ -log debug

BIN
reltools/.DS_Store vendored

Binary file not shown.

33
src/BrowsingHistory.cpp

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
#include "BrowsingHistory.h"
BrowsingHistory::BrowsingHistory() :
history(),
currentPosition(0) {}
void BrowsingHistory::addEntry(URL url) {
if (!history.empty()) {
++currentPosition;
}
if (currentPosition < history.size()) {
history.erase(history.begin() + currentPosition, history.end());
}
history.push_back(url);
}
URL const& BrowsingHistory::goForward() {
if (currentPosition < history.size()) {
++currentPosition;
}
return history[currentPosition];
}
URL const& BrowsingHistory::goBack() {
if (currentPosition > 0) {
--currentPosition;
}
return history[currentPosition];
}
std::vector<URL> const& BrowsingHistory::getHistory() const {
return history;
}

22
src/BrowsingHistory.h

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
#ifndef BROWSINGHISTORY_H
#define BROWSINGHISTORY_H
#include <vector>
#include "URL.h"
// if it's going to have fwd/back then
// please model after https://developer.mozilla.org/en-US/docs/Web/API/History
class BrowsingHistory {
public:
BrowsingHistory();
void addEntry(URL url);
URL const& goForward();
URL const& goBack();
std::vector<URL> const& getHistory() const;
private:
std::vector<URL> history;
unsigned int currentPosition;
};
#endif

41
src/CommandLineParams.cpp

@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
#include "CommandLineParams.h"
#include <vector>
namespace {
std::vector<std::string> params;
}
void initCLParams(int argc, char *argv[]) {
params = std::vector<std::string> (argv, argv + argc);
}
bool getCLParam(std::string const& param) {
for (auto const& p : params) {
if (p == param) {
return true;
}
}
return false;
}
std::string getCLParamByIndex(unsigned int i) {
if (i < params.size()) {
return params[i];
}
return "";
}
std::string getRelativeCLParam(std::string const& param, int i) {
for (unsigned int index = 0; index < params.size(); ++index) {
if (params[index] == param) {
int si = static_cast<int>(index);
unsigned int finalPos = static_cast<unsigned int>(si + i);
if (finalPos < params.size()) {
return params[finalPos];
}
return "";
}
}
return "";
}

17
src/CommandLineParams.h

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
#ifndef COMMANDLINEPARAMS_H
#define COMMANDLINEPARAMS_H
#include <string>
void initCLParams(int argc, char *argv[]);
// USAGE: getCLParam("-example")
bool getCLParam(std::string const& param);
// 0 is the program name
std::string getCLParamByIndex(unsigned int i);
// USAGE: getRelativeCLParam("-example", 1) gives the param after -example
std::string getRelativeCLParam(std::string const& param, int i);
#endif

80
src/Log.cpp

@ -0,0 +1,80 @@ @@ -0,0 +1,80 @@
#include "Log.h"
#include "CommandLineParams.h"
#include <streambuf>
#include <map>
#include "StringUtils.h"
namespace {
class NoStream : public std::streambuf {
public:
int overflow(int c) { return c; }
};
NoStream noStream;
std::ostream nothing (&noStream);
std::map<LogType, Logger> loggerCache;
std::ostream& log(LogType lt) {
auto cacheEntry = loggerCache.find(lt);
if (cacheEntry == loggerCache.end()) {
auto& outStream = ((lt == LogType::ERROR) ? std::cerr : std::cout);
auto newEntry = std::make_pair(lt, Logger(lt, outStream));
loggerCache.insert(newEntry);
}
return loggerCache[lt]();
}
}
Logger::Logger() :
logType(LogType::DBUG),
outStream(std::cout),
shouldLog(true) {
}
Logger::Logger(LogType lt, std::ostream& out) :
logType(lt),
outStream(out),
shouldLog(false) {
std::string specifiedLog = toLowercase(getRelativeCLParam("-log", 1));
LogType logLevel = LogType::ERROR;
if (specifiedLog == "warning") {
logLevel = LogType::WARNING;
} else if (specifiedLog == "info") {
logLevel = LogType::INFO;
} else if (specifiedLog == "debug") {
logLevel = LogType::DBUG;
}
if (lt <= logLevel) {
shouldLog = true;
}
}
std::ostream& Logger::operator()() {
if (shouldLog) {
return outStream;
}
return nothing;
}
std::ostream& logError() {
return log(LogType::ERROR);
}
std::ostream& logWarning() {
return log(LogType::WARNING);
}
std::ostream& logNotice() {
return log(LogType::NOTICE);
}
std::ostream& logInfo() {
return log(LogType::INFO);
}
std::ostream& logDebug() {
return log(LogType::DBUG);
}

53
src/Log.h

@ -0,0 +1,53 @@ @@ -0,0 +1,53 @@
#ifndef LOG_H
#define LOG_H
#include <iostream>
/*
USAGE:
The functions provided returns standard output/error streams depending
on the log level specified at startup. Example usage:
logWarning() << "WARNING WARNING" << std::endl;
You can create custom logger objects if you want different output
streams. You can then invoke these objects with function syntax:
Logger loggg (LogLevel::INFO, std::cerr);
loggg() << "HEY" << std::endl;
Use the command line flag "-log <logtype>" to print logtypes higher
than error. For example, if you specify "-log info", then the program
will print out info, and all lower prioritized logtypes (warning+error).
*/
// I'd like logging macro(s) that auto include __FILE__ and __LINE__ for where the output is coming from (would be cool if we could include function/method name (and class if method)) - odilitime 17/08/02
enum class LogType {
ERROR, // issue
WARNING, // potential issue
NOTICE, // basic user messages, Default include NOTICE, WARNING, ERROR
INFO, // basic 10k ft overview of what's going on
DBUG, // can't be DEBUG in clang
CALLLIST // track each function call entrance and exit
};
// network logging should be separate
class Logger {
public:
Logger();
Logger(LogType lt, std::ostream& out);
std::ostream& operator()();
private:
LogType logType;
std::ostream& outStream;
bool shouldLog;
};
// can we have some sort of macro to make these noOPs if compile as such
// no need to compile in all the debug logging calls in release
std::ostream& logError();
std::ostream& logWarning();
std::ostream& logNotice();
std::ostream& logInfo();
std::ostream& logDebug();
#endif

64
src/StringUtils.cpp

@ -1,13 +1,20 @@ @@ -1,13 +1,20 @@
#include "StringUtils.h"
#include <algorithm>
#include <iostream>
#include <iterator>
#include <algorithm>
/**
* get an extension from a filename
* @param a filename string
* @param fileName a filename string
* @return '' or a string with the found extension
*/
std::string getFilenameExtension(std::string const& fileName) {
const std::string getFilenameExtension(std::string const& fileName) {
auto dotPos = fileName.find_last_of('.');
if (dotPos != std::string::npos && dotPos + 1 != std::string::npos) {
//std::cout << "StringUtils::getFilenameExtension - " << fileName.substr(dotPos + 1) << std::endl;
return fileName.substr(dotPos + 1);
}
return "";
@ -15,7 +22,7 @@ std::string getFilenameExtension(std::string const& fileName) { @@ -15,7 +22,7 @@ std::string getFilenameExtension(std::string const& fileName) {
/**
* get an document from a url
* @param url string
* @param url url string
* @return '' or a string with the found document
*/
const std::string getDocumentFromURL(const std::string &url) {
@ -33,20 +40,51 @@ const std::string getDocumentFromURL(const std::string &url) { @@ -33,20 +40,51 @@ const std::string getDocumentFromURL(const std::string &url) {
/**
* get host from a url
* @param url string
* @param url url string
* @return '' or a string with the found host
*/
const std::string getHostFromURL(const std::string &url) {
int slashes = 0;
unsigned int start = 0;
for (unsigned int i = 0; i < url.length(); i++) {
if (url[i] == '/') {
if (slashes == 2) {
return url.substr(start, i - start);
}
slashes++;
start = i + 1;
auto colonDoubleSlash = url.find("://");
if (colonDoubleSlash != std::string::npos) {
auto startPos = colonDoubleSlash + 3;
auto thirdSlash = url.find("/", startPos);
if (thirdSlash == std::string::npos) {
return url.substr(startPos);
}
return url.substr(startPos, thirdSlash - startPos);
}
return "";
}
/**
* get scheme from a url
* @param url url string
* @return '' or a string with the found protocol
*/
const std::string getSchemeFromURL(const std::string &url) {
auto colonDoubleSlash = url.find("://");
if (colonDoubleSlash != std::string::npos) {
return url.substr(0, colonDoubleSlash);
}
return "";
}
/**
* convert string to lowercase
* @param str string
* @return lowercased version of str
*/
const std::string toLowercase(const std::string &str) {
std::string returnString = "";
std::transform(str.begin(),
str.end(),
back_inserter(returnString),
tolower);
return returnString;
/*
std::string stringToLower(std::string s) {
std::transform(s.begin(), s.end(), s.begin(),
[](unsigned char c){ return std::tolower(c); });
return s;
*/
}

5
src/StringUtils.h

@ -2,9 +2,12 @@ @@ -2,9 +2,12 @@
#define STRINGUTILS_H
#include <string>
const std::string toLowercase(const std::string &str);
std::string getFilenameExtension(std::string const& fileName);
// should be moved to URL
const std::string getFilenameExtension(std::string const& fileName);
const std::string getDocumentFromURL(const std::string &url);
const std::string getHostFromURL(const std::string &url);
const std::string getSchemeFromURL(const std::string &url);
#endif

267
src/URL.cpp

@ -0,0 +1,267 @@ @@ -0,0 +1,267 @@
#include "URL.h"
#include "StringUtils.h"
/*
* From the following RFC: https://tools.ietf.org/html/rfc3986
*
* pct-encoded = "%" HEXDIG HEXDIG
* reserved = gen-delims / sub-delims
* gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
* / "*" / "+" / "," / ";" / "="
*
*/
enum uri_parse_state {
SCHEME,
FIRST_SLASH,
SECOND_SLASH_OR_ELSE,
AUTHORITY,
AUTHORITY_USERINFO, /* The part before '@' */
AUTHORITY_PASSWORD, /* RFC states that we should either reject or ignore it (We ignore it) */
AUTHORITY_HOST,
AUTHORITY_PORT,
PATH,
QUERY,
FRAGMENT,
};
std::unique_ptr<URL> parseUri(std::string raw) {
std::unique_ptr<URL> uri = std::make_unique<URL>();
uri->path = "/";
unsigned int cursor = 0;
unsigned int last = 0;
unsigned int last_semicolon = 0;
enum uri_parse_state state = SCHEME;
// First character of scheme MUST be alphabetic
if (!isalpha(raw[cursor])) {
std::cout << "parse scheme error" << std::endl;
return NULL;
}
for (cursor = 1; cursor < raw.length(); cursor++) {
/* TODO
* Allow scheme-less uri (and fallback to https/http) */
if (state == SCHEME) {
if (raw[cursor] == ':') {
uri->scheme = toLowercase(raw.substr(0, cursor));
/* TODO
* Put default port now (Should use a table for that but
* I don't know C++ enough) */
if (uri->scheme == "http") {
uri->port = 80;
}
state = FIRST_SLASH;
} else if (!isalpha(raw[cursor]) && !isdigit(raw[cursor]) && raw[cursor] != '+' &&
raw[cursor] != '-' && raw[cursor] != '.') {
std::cout << "parse scheme error" << std::endl;
return NULL;
}
} else if (state == FIRST_SLASH) {
if (raw[cursor] == '/') {
state = SECOND_SLASH_OR_ELSE;
} else {
std::cout << "parse error" << std::endl;
}
} else if (state == SECOND_SLASH_OR_ELSE) {
if (raw[cursor] == '/') {
last = cursor + 1;
state = AUTHORITY;
} else {
// TODO Handle this, URI may have only one slash
}
} else if (state == AUTHORITY) {
/* At this point, this could either be the semi colon for
* the password or for the port*/
if (raw[cursor] == ':') {
last_semicolon = cursor;
} else if (raw[cursor] == '@') {
uri->userinfo = raw.substr(last, cursor - last);
last = cursor + 1;
state = AUTHORITY_HOST;
// Authority is finished, everything should be considered as the host[port].
// TODO terminated by the next slash ("/"), question mark ("?"), or number sign ("#") character, or by the end of the URI.
// What to do when ? and # ?
} else if (raw[cursor] == '/') {
if (last_semicolon > 0) {
// TODO Validate port
if (cursor - last_semicolon - 1 > 0) {
uri->port = std::stoi(raw.substr(last_semicolon+1, cursor - last_semicolon+1));
}
uri->host = raw.substr(last, last_semicolon - last);
} else {
uri->host = raw.substr(last, cursor - last);
}
last = cursor;
cursor--;
state = PATH;
} else if (raw[cursor] == '?' || raw[cursor] == '#') {
uri->host = raw.substr(last, cursor - last);
last = cursor;
if (raw[cursor] == '?') {
state = QUERY;
} else {
state = FRAGMENT;
}
} else if (cursor + 1 == raw.length()) {
uri->host = raw.substr(last, last_semicolon - last);
uri->path = "/";
break;
}
} else if (state == AUTHORITY_HOST) {
if (raw[cursor] == ':') {
uri->host = raw.substr(last, cursor - last);
last = cursor+1;
state = AUTHORITY_PORT;
} else if (raw[cursor] == '/') {
uri->host = raw.substr(last, cursor - last);
last = cursor;
cursor--;
state = PATH;
}
} else if (state == AUTHORITY_PORT) {
if (raw[cursor] == '/') {
if (cursor - last > 0) {
uri->port = std::stoi(raw.substr(last, cursor - last));
}
last = cursor;
cursor--;
state = PATH;
} else if (!isdigit(raw[cursor])) {
}
} else if (state == PATH) {
if (raw[cursor] == '?' || raw[cursor] == '#') {
uri->path = raw.substr(last, cursor - last);
last = cursor;
if (raw[cursor] == '?') {
state = QUERY;
} else {
state = FRAGMENT;
}
} else if (cursor + 1 == raw.length()) {
uri->path = raw.substr(last, cursor + 1 - last);
break;
}
} else if (state == QUERY) {
if (raw[cursor] == '#') {
uri->query = raw.substr(last + 1, cursor - last - 1);
last = cursor;
state = FRAGMENT;
} else if (cursor + 1 == raw.length()) {
uri->query = raw.substr(last + 1, cursor + 1 - last);
break;
}
} else if (state == FRAGMENT) {
if (cursor + 1 == raw.length()) {
uri->fragment = raw.substr(last + 1, cursor + 1 - last);
break;
}
}
}
return uri;
}
URL::URL() {
scheme = "";
userinfo = "";
host = "";
port = 0;
path = "";
query = "";
fragment = "";
}
URL::URL(std::string const& url) {
construct(url);
}
std::string URL::toString() const {
if (isRelative()) {
return path;
}
return scheme + "://" + host + path;
}
bool URL::isRelative() const {
return scheme.size() == 0;
}
URL URL::merge(URL const& url) const {
if (!url.isRelative()) {
return url.copy();
}
URL returnURL = copy();
if (url.path[0] == '/' && url.path[1] == '/') {
auto slashPos = url.path.find('/', 2);
returnURL.host = url.path.substr(2, slashPos - 2);
if (slashPos == std::string::npos) {
returnURL.path = "/";
} else {
returnURL.path = url.path.substr(slashPos);
}
} else if (url.path[0] == '/') {
returnURL.path = url.path;
} else {
if (returnURL.path.back() != '/') {
auto finalSlashPos = returnURL.path.find_last_of('/');
returnURL.path.erase(finalSlashPos + 1);
}
returnURL.path += url.path;
}
/*
auto hashPos = returnURL.document.find("#");
if (hashPos != std::string::npos) {
returnURL.document = returnURL.document.substr(0, hashPos);
}
*/
return returnURL;
}
void URL::construct(std::string const& url) {
std::unique_ptr<URL> uri=parseUri(url);
scheme = uri->scheme;
userinfo = uri->userinfo;
host = uri->host;
port = uri->port;
path = uri->path;
query = uri->query;
fragment = uri->fragment;
/*
scheme = getSchemeFromURL(url);
if (scheme.size() != 0) {
host = getHostFromURL(url);
path = getDocumentFromURL(url);
} else {
host = "";
path = url;
}
if (path.size() == 0) {
path = "/";
}
*/
}
URL URL::copy() const {
URL url;
url.scheme = scheme;
url.userinfo = userinfo;
url.host = host;
url.port = port;
url.path = path;
url.query = query;
url.fragment = fragment;
return url;
}
std::ostream& operator<<(std::ostream& out, URL const& url) {
out << url.toString();
return out;
}

40
src/URL.h

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
#ifndef URL_H
#define URL_H
#include <string>
#include <memory>
#include <iostream>
struct Authority {
std::string userinfo;
std::string host;
int port;
};
struct URL {
URL();
URL(std::string const& url);
std::string toString() const;
bool isRelative() const;
URL merge(URL const& url) const;
// switching to the official RFC names
std::string scheme; // was protocol
//struct Authority authority; /* Can be empty */
std::string userinfo;
std::string host;
int port;
std::string path; // was document
std::string query; // ?blablabla=asdfasf....
std::string fragment; // #asfawefm
private:
void construct(std::string const& url);
URL copy() const;
};
std::unique_ptr<URL> parseUri(std::string raw);
std::ostream& operator<<(std::ostream& out, URL const& url);
#endif

72
src/WebResource.cpp

@ -6,6 +6,8 @@ @@ -6,6 +6,8 @@
#include "networking/HTTPRequest.h"
#include "networking/HTTPResponse.h"
#include "StringUtils.h"
#include <iostream>
#include "Log.h"
namespace {
@ -16,8 +18,8 @@ namespace { @@ -16,8 +18,8 @@ namespace {
{"js", ResourceType::JS}
};
bool isOnlineResource(std::string const& resourceName) {
return resourceName.find("://") != std::string::npos && resourceName.find("file://") == std::string::npos;
bool isOnlineResource(URL const& url) {
return url.scheme != "file";
}
}
@ -32,15 +34,17 @@ WebResource::WebResource(ResourceType rtype, std::string const& rraw) { @@ -32,15 +34,17 @@ WebResource::WebResource(ResourceType rtype, std::string const& rraw) {
raw = rraw;
}
WebResource getWebResource(std::string resourceName) {
if (isOnlineResource(resourceName)) {
return getOnlineWebResource(resourceName);
WebResource getWebResource(URL const& url) {
if (isOnlineResource(url)) {
//std::cout << "WebReousrce::getWebResource - isOnline" << std::endl;
return getOnlineWebResource(url);
}
return getLocalWebResource(resourceName);
//std::cout << "WebReousrce::getWebResource - isOffline" << std::endl;
return getLocalWebResource(url);
}
WebResource getLocalWebResource(std::string fileName) {
std::string fileExtension = getFilenameExtension(fileName);
WebResource getLocalWebResource(URL const& url) {
std::string fileExtension = getFilenameExtension(url.path);
if (fileExtension.length() == 0) {
return WebResource(ResourceType::INVALID,
"Could not find any file extension");
@ -54,10 +58,10 @@ WebResource getLocalWebResource(std::string fileName) { @@ -54,10 +58,10 @@ WebResource getLocalWebResource(std::string fileName) {
if (strToRT.find(fileExtension) == strToRT.end()) {
return WebResource(ResourceType::INVALID,
"Resource type " + fileExtension + " not supported");
"Local file with extension " + fileExtension + " is not supported. Did you forget a http://?");
}
std::ifstream in(fileName, std::ios::in | std::ios::binary);
std::ifstream in(url.path, std::ios::in | std::ios::binary);
if (in) {
// There exists more efficient ways of doing this, but it works for the
// time being.
@ -68,22 +72,56 @@ WebResource getLocalWebResource(std::string fileName) { @@ -68,22 +72,56 @@ WebResource getLocalWebResource(std::string fileName) {
}
return WebResource(ResourceType::INVALID,
"Could not open file " + fileName);
"Could not open file " + url.path);
}
WebResource getOnlineWebResource(std::string url) {
HTTPRequest request (getHostFromURL(url), getDocumentFromURL(url));
WebResource getOnlineWebResource(URL const& url) {
std::shared_ptr<URL> uri=std::make_shared<URL>(url);
HTTPRequest request (uri);
//window->currentURL=url;
//request->sendRequest(handleRequest);
WebResource returnRes;
std::string redirectLocation = "";
request.sendRequest([&](HTTPResponse const& response){
if (response.statusCode != 200) {
logDebug() << "getOnlineWebResource request.sendRequest" << std::endl;
if (response.statusCode == 301) {
std::string location;
if (response.properties.find("Location")==response.properties.end()) {
if (response.properties.find("location")==response.properties.end()) {
logDebug() << "getOnlineWebResource - got 301 without a location" << std::endl;
for(auto const &row : response.properties) {
logDebug() << "getOnlineWebResource - " << row.first << "=" << response.properties.at(row.first) << std::endl;
}
redirectLocation = "_";
} else {
location = response.properties.at("location");
}
} else {
location = response.properties.at("Location");
}
logDebug() << "Redirect To: " << location << std::endl;
redirectLocation = location;
} else if (response.statusCode != 200) {
returnRes.resourceType = ResourceType::INVALID;
returnRes.raw = "Unsupported status code";
} else {
// TODO: Set resourceType based on Content-Type field.
returnRes.resourceType = ResourceType::HTML;
returnRes.raw = std::move(response.body);
}
// TODO: Set resourceType based on Content-Type field.
returnRes.resourceType = ResourceType::HTML;
returnRes.raw = std::move(response.body);
});
if (redirectLocation.size() > 0) {
if (redirectLocation == "_") {
return WebResource(ResourceType::INVALID,
"Got a 301 without a location");
}
return getOnlineWebResource(URL(redirectLocation));
}
return returnRes;
}

7
src/WebResource.h

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
#define WEBRESOURCE_H
#include <string>
#include "URL.h"
enum class ResourceType {
INVALID,
@ -20,12 +21,12 @@ struct WebResource { @@ -20,12 +21,12 @@ struct WebResource {
// Depending on the resourceName specified, this function will forward the call
// to either getLocalWebResource or getOnlineWebResource.
WebResource getWebResource(std::string resourceName);
WebResource getWebResource(URL const& url);
// Loads a resource from the local file storage.
WebResource getLocalWebResource(std::string fileName);
WebResource getLocalWebResource(URL const& url);
// Loads a resource from an internet address.
WebResource getOnlineWebResource(std::string url);
WebResource getOnlineWebResource(URL const& url);
#endif

4
src/graphics/components/AnimeComponent.cpp

@ -1,5 +1,7 @@ @@ -1,5 +1,7 @@
#include "AnimeComponent.h"
#ifndef _MSC_VER
#include "../../../anime.h"
#endif
#include <cmath>
AnimeComponent::AnimeComponent(const float rawX, const float rawY, const float rawWidth, const float rawHeight, const int passedWindowWidth, const int passedWindowHeight) : BoxComponent(rawX, rawY, rawWidth, rawHeight, passedWindowWidth, passedWindowHeight){
@ -8,6 +10,7 @@ AnimeComponent::AnimeComponent(const float rawX, const float rawY, const float r @@ -8,6 +10,7 @@ AnimeComponent::AnimeComponent(const float rawX, const float rawY, const float r
width = rawWidth;
height = rawHeight;
#ifndef _MSC_VER
if (width == 512) {
for (int py = 0; py < 1024; py++) {
for (int px = 0; px < 1024; px++) {
@ -17,6 +20,7 @@ AnimeComponent::AnimeComponent(const float rawX, const float rawY, const float r @@ -17,6 +20,7 @@ AnimeComponent::AnimeComponent(const float rawX, const float rawY, const float r
}
}
}
#endif
float vx = rawX;
float vy = rawY;

1
src/graphics/components/Component.cpp

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
#include "Component.h"
#include <iostream>
#include <algorithm>
#include "TextComponent.h"
Component::~Component() {

2
src/graphics/components/Component.h

@ -84,7 +84,7 @@ public: @@ -84,7 +84,7 @@ public:
unsigned int bottomPadding = 0;
// x + left margin + left padding = start x for texture
// size preference (could also prefer t/b, l/r)
bool growRight = false;
bool growRight = false; // if less than windowWidth, why not be greedy?
bool growLeft = false;
bool growTop = false; // if top/bott grow from center
bool growBottom = false;

5
src/graphics/elements/AElement.cpp

@ -1,6 +1,9 @@ @@ -1,6 +1,9 @@
#include "AElement.h"
#include "../../html/TagNode.h"
#include <iostream>
#include "../opengl/Window.h"
extern const std::unique_ptr<Window> window;
AElement::AElement() {
isInline = true;
@ -16,7 +19,7 @@ std::unique_ptr<Component> AElement::renderer(const std::shared_ptr<Node> node, @@ -16,7 +19,7 @@ std::unique_ptr<Component> AElement::renderer(const std::shared_ptr<Node> node,
if (hrefPair != tagNode->properties.end()) {
component->onClick = [hrefPair]() {
std::cout << "Direct to: " << hrefPair->second << std::endl;
navTo(hrefPair->second);
window->navTo(hrefPair->second);
};
}
}

89
src/graphics/opengl/Window.cpp

@ -3,9 +3,14 @@ @@ -3,9 +3,14 @@
#include "shaders/gen/TextureShader.h"
#include "../../html/TagNode.h"
#include "../../html/TextNode.h"
#include "../../networking/HTTPRequest.h"
#include "../../html/HTMLParser.h"
#include "StringUtils.h"
#include <cmath>
#include <ctime>
#include <iostream>
#include "URL.h"
#include "Log.h"
void deleteComponent(std::shared_ptr<Component> &component);
void deleteNode(std::shared_ptr<Node> node);
@ -396,3 +401,87 @@ std::shared_ptr<Component> Window::searchComponentTree(const std::shared_ptr<Com @@ -396,3 +401,87 @@ std::shared_ptr<Component> Window::searchComponentTree(const std::shared_ptr<Com
}
return nullptr;
}
// moving naviagtion closer to window, as window is now the owner of currentURL
// preparation for multiple HTML documents
void Window::navTo(std::string url) {
logDebug() << "navTo(" << url << ")" << std::endl;
currentURL = currentURL.merge(URL(url));
logDebug() << "go to: " << currentURL << std::endl;
setWindowContent(currentURL);
}
/*
void handleRequest(const HTTPResponse &response) {
std::cout << "main:::handleRequest - statusCode: " << response.statusCode << std::endl;
if (response.statusCode == 200) {
const std::unique_ptr<HTMLParser> parser = std::make_unique<HTMLParser>();
const std::clock_t begin = clock();
std::shared_ptr<Node> rootNode = parser->parse(response.body);
const std::clock_t end = clock();
std::cout << "Parsed document in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
window->setDOM(rootNode);
}
else if (response.statusCode == 301) {
std::string location;
if (response.properties.find("Location")==response.properties.end()) {
if (response.properties.find("location")==response.properties.end()) {
std::cout << "::handleRequest - got 301 without a location" << std::endl;
for(auto const &row : response.properties) {
std::cout << "::handleRequest - " << row.first << "=" << response.properties.at(row.first) << std::endl;
}
return;
} else {
location = response.properties.at("location");
}
} else {
location = response.properties.at("Location");
}
std::cout << "Redirect To: " << location << std::endl;
std::shared_ptr<URI> uri = parseUri(location);
const std::unique_ptr<HTTPRequest> request = std::make_unique<HTTPRequest>(uri);
request->sendRequest(handleRequest);
return;
}
else {
std::cout << "Unknown Status Code: " << response.statusCode << std::endl;
}
}
*/
// tried to make a window method
void handleRequest(const HTTPResponse &response) {
std::cout << "main:::handleRequest - statusCode: " << response.statusCode << std::endl;
if (response.statusCode == 200) {
const std::unique_ptr<HTMLParser> parser = std::make_unique<HTMLParser>();
const std::clock_t begin = clock();
std::shared_ptr<Node> rootNode = parser->parse(response.body);
const std::clock_t end = clock();
std::cout << "Parsed document in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
window->setDOM(rootNode);
}
else if (response.statusCode == 301) {
std::string location;
if (response.properties.find("Location")==response.properties.end()) {
if (response.properties.find("location")==response.properties.end()) {
std::cout << "::handleRequest - got 301 without a location" << std::endl;
for(auto const &row : response.properties) {
std::cout << "::handleRequest - " << row.first << "=" << response.properties.at(row.first) << std::endl;
}
return;
} else {
location = response.properties.at("location");
}
} else {
location = response.properties.at("Location");
}
std::cout << "Redirect To: " << location << std::endl;
std::shared_ptr<URL> uri = parseUri(location);
const std::unique_ptr<HTTPRequest> request = std::make_unique<HTTPRequest>(uri);
request->sendRequest(handleRequest);
return;
}
else {
std::cout << "Unknown Status Code: " << response.statusCode << std::endl;
}
}

10
src/graphics/opengl/Window.h

@ -12,7 +12,8 @@ @@ -12,7 +12,8 @@
#include <memory>
#include <vector>
#include <algorithm>
#include "../../networking/HTTPResponse.h"
#include "../../URL.h"
class Window {
private:
@ -33,6 +34,8 @@ public: @@ -33,6 +34,8 @@ public:
void renderComponents(std::shared_ptr<Component> component);
void resizeComponentTree(const std::shared_ptr<Component> &component, const int windowWidth, const int windowHeight);
std::shared_ptr<Component> searchComponentTree(const std::shared_ptr<Component> &component, const int x, const int y);
void navTo(std::string url);
// properties
float transformMatrix[16] = {
1, 0, 0, 0,
0, 1, 0, 0,
@ -55,6 +58,11 @@ public: @@ -55,6 +58,11 @@ public:
GLFWcursor* cursorHand;
GLFWcursor* cursorArrow;
GLFWcursor* cursorIbeam;
URL currentURL;
};
bool setWindowContent(URL const& url);
void handleRequest(const HTTPResponse &response);
extern const std::unique_ptr<Window> window;
#endif

13
src/html/Node.cpp

@ -6,3 +6,16 @@ Node::Node(NodeType type) { @@ -6,3 +6,16 @@ Node::Node(NodeType type) {
Node::~Node() {
}
std::vector<std::string> Node::getSourceList() {
std::vector<std::string> returnVec;
for (std::shared_ptr<Node>& child : children) {
auto childSrcs = child->getSourceList();
returnVec.insert(returnVec.end(),
childSrcs.begin(),
childSrcs.end());
}
return returnVec;
}

2
src/html/Node.h

@ -23,6 +23,8 @@ public: @@ -23,6 +23,8 @@ public:
std::shared_ptr<Node> parent;
std::vector<std::shared_ptr<Node>> children;
std::shared_ptr<Component> component;
virtual std::vector<std::string> getSourceList();
};
#endif

18
src/html/TagNode.cpp

@ -2,3 +2,21 @@ @@ -2,3 +2,21 @@
TagNode::TagNode() : Node(NodeType::TAG) {
}
std::vector<std::string> TagNode::getSourceList() {
std::vector<std::string> returnVec;
auto propIter = properties.find("src");
if (propIter != properties.end()) {
returnVec.push_back(propIter->second);
}
for (std::shared_ptr<Node>& child : children) {
auto childSrcs = child->getSourceList();
returnVec.insert(returnVec.end(),
childSrcs.begin(),
childSrcs.end());
}
return returnVec;
}

2
src/html/TagNode.h

@ -9,6 +9,8 @@ public: @@ -9,6 +9,8 @@ public:
TagNode();
std::string tag;
std::map<std::string, std::string> properties;
std::vector<std::string> getSourceList() override;
};
#endif

2
src/html/TextNode.h

@ -7,6 +7,8 @@ class TextNode : public Node { @@ -7,6 +7,8 @@ class TextNode : public Node {
public:
TextNode();
std::string text;
std::vector<std::string> getSourceList() override { return {}; };
};
#endif

154
src/main.cpp

@ -1,149 +1,51 @@ @@ -1,149 +1,51 @@
#include "graphics/opengl/Window.h"
#include "html/HTMLParser.h"
#include "html/TagNode.h"
#include "html/TextNode.h"
#include "networking/HTTPRequest.h"
#include "networking/HTTPResponse.h"
#include <ctime>
#include <iostream>
#include <memory>
#include "StringUtils.h"
#include "WebResource.h"
void handleRequest(const HTTPResponse &response);
#include "CommandLineParams.h"
#include "URL.h"
#include "Log.h"
const std::unique_ptr<Window> window = std::make_unique<Window>();
std::string currentURL="";
//URL currentURL;
void navTo(std::string url) {
std::cout << "go to: " << url << std::endl;
//
// All of this needs to be redone correctly
// this is just temporary hack code, so I can get to specific pages for debugging
//
const std::size_t dSlashPos=url.find("//");
if (dSlashPos!=std::string::npos) {
// remote
// check for relative
if (dSlashPos==0) {
// we only support http atm
url="http:"+url;
}
} else {
// https://github.com/unshiftio/url-parse/blob/master/index.js#L111
//auto const lastSlashPos=url.find_last_of('/');
// could collapse ./|/. to . and /./ to /
if (url=="." || url=="./") {
auto const lastSlashPos=getDocumentFromURL(currentURL).find_last_of('/');
if (lastSlashPos!=std::string::npos) {
url="http://"+getHostFromURL(currentURL)+getDocumentFromURL(currentURL).substr(0, lastSlashPos)+'/';
}
} else {
if (url[0]=='/') {
// absolute URL
url="http://"+getHostFromURL(currentURL)+url;
} else {
// relative URL
const std::string path=getDocumentFromURL(currentURL);
std::cout << "old path: " << path << std::endl;
if (url.length()>1 && url[0]=='.' && url[1]=='.') {
// are we at / ?
if (path=="/") {
// if so, remove ../
if (url.length()>2) {
url=url.substr(3);
} else {
url=url.substr(2);
}
url=path+url;
} else {
// .. but we're not in /
// make sure not a filename
auto const inDir=path.find_last_of("/");
if (inDir!=std::string::npos) {
// now take off a directory
auto const inDir2=path.substr(0, inDir).find_last_of("/");
url=path.substr(0, inDir2+1)+url.substr(2);
} else {
url=path+url;
}
}
} else {
url=path+url;
}
url="http://"+getHostFromURL(currentURL)+url;
}
}
}
// strip # off
std::size_t hashPos=url.find("#");
if (hashPos!=std::string::npos) {
url=url.substr(0, hashPos);
bool setWindowContent(URL const& url) {
logDebug() << "main::setWindowContent - " << url << std::endl;
WebResource res = getWebResource(url);
if (res.resourceType == ResourceType::INVALID) {
logError() << "Invalid resource type: " << res.raw << std::endl;
return false;
}
std::cout << "go to: " << url << std::endl;
std::shared_ptr<Node> rootNode = std::make_shared<Node>(NodeType::ROOT);
HTMLParser parser;
const std::clock_t begin = clock();
std::shared_ptr<Node> rootNode = parser.parse(res.raw);
const std::clock_t end = clock();
logDebug() << "Parsed document in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
window->setDOM(rootNode);
const std::unique_ptr<HTTPRequest> request = std::make_unique<HTTPRequest>(getHostFromURL(url), getDocumentFromURL(url));
currentURL=url;
request->sendRequest(handleRequest);
}
void handleRequest(const HTTPResponse &response) {
std::cout << "main:::handleRequest - statusCode: " << response.statusCode << std::endl;
if (response.statusCode == 200) {
const std::unique_ptr<HTMLParser> parser = std::make_unique<HTMLParser>();
const std::clock_t begin = clock();
std::shared_ptr<Node> rootNode = parser->parse(response.body);
const std::clock_t end = clock();
std::cout << "Parsed document in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
window->setDOM(rootNode);
}
else if (response.statusCode == 301) {
std::string location;
if (response.properties.find("Location")==response.properties.end()) {
if (response.properties.find("location")==response.properties.end()) {
std::cout << "::handleRequest - got 301 without a location" << std::endl;
for(auto const &row : response.properties) {
std::cout << "::handleRequest - " << row.first << "=" << response.properties.at(row.first) << std::endl;
}
return;
} else {
location = response.properties.at("location");
}
} else {
location = response.properties.at("Location");
}
std::cout << "Redirect To: " << location << std::endl;
const std::unique_ptr<HTTPRequest> request = std::make_unique<HTTPRequest>(getHostFromURL(location), getDocumentFromURL(location));
request->sendRequest(handleRequest);
return;
}
else {
std::cout << "Unknown Status Code: " << response.statusCode << std::endl;
}
return true;
}
int main(int argc, char *argv[]) {
if (argc == 1) {
std::cout << "./netrunner <url|file.html>" << std::endl;
std::cout << "./netrunner <url|file.html> [-log <error|warning|notice|info|debug>]" << std::endl;
return 1;
}
std::cout << "/g/ntr - NetRunner build " << __DATE__ << std::endl;
currentURL=argv[1];
WebResource res = getWebResource(currentURL);
if (res.resourceType == ResourceType::INVALID) {
std::cout << "Invalid resource type" << std::endl;
initCLParams(argc, argv);
std::string url = getCLParamByIndex(1);
if (url[0] == '/') {
url = "file://" + url;
}
window->currentURL=URL(url);
logDebug() << "loading [" << window->currentURL << "]" << std::endl;
if (!setWindowContent(window->currentURL)) {
return 1;
}
HTMLParser parser;
const std::clock_t begin = clock();
std::shared_ptr<Node> rootNode = parser.parse(res.raw);
const std::clock_t end = clock();
std::cout << "Parsed document in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
window->setDOM(rootNode);
window->init();
if (!window->window) {
return 1;

34
src/networking/HTTPRequest.cpp

@ -1,27 +1,51 @@ @@ -1,27 +1,51 @@
#include "HTTPRequest.h"
#include <errno.h>
#include <iostream>