diff --git a/mkspecs/features/qt.prf b/mkspecs/features/qt.prf
index f62b6bb1399fef3af0ba3b4e3fe3e0f1a37f1c19..06d1afe5ea92ea1e6f49f3e4326d5441fe36cc82 100644
--- a/mkspecs/features/qt.prf
+++ b/mkspecs/features/qt.prf
@@ -278,6 +278,31 @@ contains(qt_module_deps, qml): \
                     "rsync -r --exclude='*.a' --exclude='*.prl' --exclude='*.qmltypes' "
             macx-xcode: QMAKE_POST_LINK += "$p/ $$qmlTargetPath; done"
             else: QMAKE_POST_LINK += "\$\$p/ $$qmlTargetPath; done"
+        } else:emscripten {
+            isEmpty(QMAKE_QML_BUNDLE_PATH):QMAKE_QML_BUNDLE_PATH = "qt_qml"
+            qmlTargetPath = $$OUT_PWD/$$QMAKE_QML_BUNDLE_PATH
+            qtconfTargetPath = $$OUT_PWD/qt.conf
+
+            # set import path in qt.conf to point to the bundeled qml:
+            QT_CONF_CONTENTS = \
+                "[Paths]" \
+                "Imports = $$QMAKE_QML_BUNDLE_PATH" \
+                "Qml2Imports = $$QMAKE_QML_BUNDLE_PATH"
+            write_file("$$OUT_PWD/qt.conf", QT_CONF_CONTENTS)|error("Aborting.")
+
+            # write qt.conf and copy each qml import dir into the bundle.
+            # But strip away archives and other files that are not needed:
+            !isEmpty(QMAKE_POST_LINK): QMAKE_POST_LINK += ";"
+            QMAKE_POST_LINK += \
+                "test -d $$qmlTargetPath && rm -r $$qmlTargetPath; " \
+                "mkdir -p $$qmlTargetPath && " \
+                "for p in $$QMLPATHS; do" \
+                    "rsync -r --exclude='*.a' --exclude='*.so' --exclude='*.prl' --exclude='*.qmltypes' " \
+                    "\$\$p/ $$qmlTargetPath; done"
+
+            # Add the folder and qt.conf to be preloaded by emscripten
+            QMAKE_LFLAGS += --preload-file qt_qml
+            QMAKE_LFLAGS += --preload-file qt.conf
         }
     }
 }
diff --git a/mkspecs/unsupported/nacl-emscripten/qmake.conf b/mkspecs/unsupported/nacl-emscripten/qmake.conf
index 8b28a753a44d6b44f3a6ec2e618abcd12b0fc5bc..819ab58b72fbedd3b5deeadb45baa28c0a3324c9 100644
--- a/mkspecs/unsupported/nacl-emscripten/qmake.conf
+++ b/mkspecs/unsupported/nacl-emscripten/qmake.conf
@@ -2,6 +2,8 @@
 # qmake configuration for building with nacl-emscripten
 #
 
+QMAKE_INCDIR += $$(EMSCRIPTEN)/system/include
+
 include(../../common/unix.conf)
 include(../../common/gcc-base-unix.conf)
 include(../../common/g++-unix.conf)
@@ -16,22 +18,30 @@ QMAKE_LINK              = em++
 QMAKE_LINK_SHLIB        = em++
 QMAKE_AR                = emar r
 
-CONFIG += emscripten
+CONFIG *= emscripten
 
 # Uset default NaCl setting
 QMAKE_LIBS_OPENGL_ES2=
 
-# Qt adds -I/path/to/qt/include, which triggers a "may be host include"
-# warning. It isn't: silence the warning.
-QMAKE_CXXFLAGS += -Wno-warn-absolute-paths
-QMAKE_LFLAGS += -Wl,-Wno-warn-absolute-paths
+emscripten_pthreads {
+    CONFIG += emscripten_pthreads
+    QMAKE_CXXFLAGS += -s USE_PTHREADS=1 \
+
+    QMAKE_LFLAGS += -s USE_PTHREADS=1 \
+        -s PTHREAD_POOL_SIZE=4\
+
+}
+
+QMAKE_LFLAGS += -Wl
 
 # Add link-time pepper platform plugin javascript dependencies and
 # required emscripten linker options.
 PEPPER_JS_PATH=$$PWD/../../../src/plugins/platforms/pepper/3rdparty/pepper.js
+
 QMAKE_LFLAGS += \
     -s RESERVED_FUNCTION_POINTERS=400\
-    -s TOTAL_MEMORY=512000000\
+    -s TOTAL_MEMORY=1280000000\
+    -s FULL_ES2=1 \
     -s EXPORTED_FUNCTIONS=\"[\'_DoPostMessage\', \'_DoChangeView\', \'_DoChangeFocus\', \'_NativeCreateInstance\', \'_HandleInputEvent\']\"\
     --pre-js $${PEPPER_JS_PATH}/ppapi_preamble.js\
     --pre-js $${PEPPER_JS_PATH}/base.js\
diff --git a/nacl-configure b/nacl-configure
index 3dbd9fda9c9ecd341532b653b75d632f645fec52..d53bc6bec07f62ccf0063bda6236eff9f10c7a66 100755
--- a/nacl-configure
+++ b/nacl-configure
@@ -229,6 +229,10 @@ fi
 if [[ $TOOLCHAIN == emscripten ]]
   then
     NACL_CONFIGURE_LINE="$NACL_CONFIGURE_LINE -no-xlib -no-xcb-xlib -no-eglfs"
+    # emscripten needs to enforce OpenGL ES 2
+    NACL_CONFIGURE_LINE="$NACL_CONFIGURE_LINE -opengl es2"
+    # emscripten needs a static build for plugins to work properly
+    NACL_CONFIGURE_LINE="$NACL_CONFIGURE_LINE -static"
 fi
 
 # set release/debug build
@@ -247,4 +251,3 @@ echo $NACL_CONFIGURE_LINE
 
 # Run configure
 $NACL_CONFIGURE_LINE
-
diff --git a/src/corelib/global/qsystemdetection.h b/src/corelib/global/qsystemdetection.h
index 250f186926c4c93b0b32f0dd3215f8b23b2866c1..74c734e245e59143e1ababdd98b3bda9ace0ac75 100644
--- a/src/corelib/global/qsystemdetection.h
+++ b/src/corelib/global/qsystemdetection.h
@@ -330,7 +330,9 @@
 #  define Q_OS_PNACL
 #endif
 #ifdef __EMSCRIPTEN__
+#  ifndef Q_OS_NACL_EMSCRIPTEN
 #  define Q_OS_NACL_EMSCRIPTEN
+#  endif
 #endif
 #endif
 
diff --git a/src/corelib/kernel/qeventdispatcher_unix.cpp b/src/corelib/kernel/qeventdispatcher_unix.cpp
index 58164dba472691d39184c93864f5f68ed0ff30a2..60b46a35f5bf62bbb7d1de95b5b15c7347b08fd3 100644
--- a/src/corelib/kernel/qeventdispatcher_unix.cpp
+++ b/src/corelib/kernel/qeventdispatcher_unix.cpp
@@ -190,12 +190,15 @@ int QEventDispatcherUNIXPrivate::doSelect(QEventLoop::ProcessEventsFlags flags,
         int wakeUpFd = initThreadWakeUp();
         highest = qMax(highest, wakeUpFd);
 #endif
-
+#ifdef Q_OS_NACL_EMSCRIPTEN
+        nsel = 0;
+#else
         nsel = q->select(highest + 1,
                          &sn_vec[0].select_fds,
                          &sn_vec[1].select_fds,
                          &sn_vec[2].select_fds,
                          timeout);
+#endif
     } while (nsel == -1 && (errno == EINTR || errno == EAGAIN));
 
     if (nsel == -1) {
diff --git a/src/corelib/kernel/qfunctions_nacl.cpp b/src/corelib/kernel/qfunctions_nacl.cpp
index 5234452581636b742b7c1de068e3e57ac2ec8f40..1c66ea5b239ba983e9a64a429ebd2b0c9b98a6a2 100644
--- a/src/corelib/kernel/qfunctions_nacl.cpp
+++ b/src/corelib/kernel/qfunctions_nacl.cpp
@@ -82,7 +82,6 @@ void pthread_testcancel(void)
 
 }
 
-
 int pthread_cancel(pthread_t)
 {
     return 0;
@@ -140,10 +139,12 @@ int sigaction(int, const struct sigaction *, struct sigaction *)
     return 0;
 }
 
+#ifndef Q_OS_NACL_EMSCRIPTEN
 int open(const char *, int, ...)
 {
     return 0;
 }
+#endif
 
 int open64(const char *, int, ...)
 {
@@ -155,12 +156,12 @@ long pathconf(const char *, int)
     return 0;
 }
 
+#ifndef Q_OS_NACL_EMSCRIPTEN
 int access(const char *, int)
 {
     return 0;
 }
 
-#ifndef Q_OS_NACL_EMSCRIPTEN
 typedef long off64_t;
 off64_t ftello64(void *)
 {
@@ -197,8 +198,6 @@ int unsetenv(const char *name)
 }
 #endif
 
-} // Extern C
-
 int select(int, fd_set *, fd_set *, fd_set *, struct timeval *)
 {
     return 0;
@@ -209,10 +208,9 @@ int pselect(int nfds, fd_set * readfds, fd_set * writefds, fd_set * errorfds, co
     return 0;
 }
 
-#ifdef Q_OS_NACL_EMSCRIPTEN
-
-// pthread stubs (no thrading support in emscripten)
+} // Extern C
 
+#if defined(Q_OS_NACL_EMSCRIPTEN) && !defined(__EMSCRIPTEN_PTHREADS__)
 int pthread_setcancelstate(int state, int *oldstate)
 {
     return 0;
@@ -332,7 +330,6 @@ int sched_get_priority_min(int policy)
 {
     return 0;
 }
-
 #endif
 
 // Several Qt components (such at the QtCore event dispatcher and networking) 
diff --git a/src/corelib/kernel/qfunctions_nacl.h b/src/corelib/kernel/qfunctions_nacl.h
index 34e0efda1d0564a19b2dd73ff52af32300486747..915f9c4220ca5402f8761052701639a7f6f3ff70 100644
--- a/src/corelib/kernel/qfunctions_nacl.h
+++ b/src/corelib/kernel/qfunctions_nacl.h
@@ -128,11 +128,11 @@ int setenv(const char *name, const char *value, int overwrite) __attribute__((we
 int unsetenv(const char *name) __attribute__((weak));
 #endif
 
-}
-
 int select(int nfds, fd_set * readfds, fd_set * writefds, fd_set * errorfds, struct timeval * timeout) __attribute__((weak));
 int pselect(int nfds, fd_set * readfds, fd_set * writefds, fd_set * errorfds, const struct timespec * timeout, const sigset_t * sigmask) __attribute__((weak));
 
+}
+
 QT_END_NAMESPACE
 
 #endif //Q_OS_NACL
diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp
index 2df48cff345a521c03f35ad31d47d5717fbfac48..0d0a97f82e39272d0dddd336b8d4b4636f8eab62 100644
--- a/src/corelib/plugin/qlibrary.cpp
+++ b/src/corelib/plugin/qlibrary.cpp
@@ -712,7 +712,7 @@ void QLibraryPrivate::updatePluginState()
     }
 #endif
 
-#if !defined (Q_OS_NACL)
+#if !defined (Q_OS_NACL) || defined (Q_OS_NACL_EMSCRIPTEN)
     if (!pHnd) {
         // scan for the plugin metadata without loading
         success = findPatternUnloaded(fileName, this);
diff --git a/src/corelib/plugin/qplugin.h b/src/corelib/plugin/qplugin.h
index 9b3725a7184c05eb9a4608c5ab4b0636970c59d1..9af1629a13eb99888428db35ccdf2f089a570b59 100644
--- a/src/corelib/plugin/qplugin.h
+++ b/src/corelib/plugin/qplugin.h
@@ -40,7 +40,6 @@
 
 QT_BEGIN_NAMESPACE
 
-
 #ifndef Q_EXTERN_C
 #  ifdef __cplusplus
 #    define Q_EXTERN_C extern "C"
@@ -71,7 +70,20 @@ Q_DECLARE_TYPEINFO(QStaticPlugin, Q_PRIMITIVE_TYPE);
 
 void Q_CORE_EXPORT qRegisterStaticPluginFunction(QStaticPlugin staticPlugin);
 
-#ifdef Q_OS_PNACL
+#if defined(Q_OS_NACL_EMSCRIPTEN)
+// It is important that the metadata is aligned in Emscripten because we are
+// not allowed to read unaligned data in JS/Emscripten.
+// Without this, importing static plugins will randomly fail because reading
+// the offset 'size' value in QLibraryPrivate::fromRawMetaData is undefined behavior.
+// For this purpose, aligning to 32 bits would suffice.
+// Using 64 bits is probably overkill, but should ensure that this problem doesn't
+// surface in other parts of the code.
+
+// TODO Do we need "section" and "used" attributes on Emscripten?
+// TODO Should we reduce alignment to 32 bits?
+#  define QT_PLUGIN_METADATA_SECTION \
+    __attribute__ ((section (".qtmetadata"))) __attribute__((used)) __attribute__((aligned(64)))
+#elif defined(Q_OS_PNACL)
 // PNaCl does not support "section":
 // "Variable _ZL17qt_pluginMetaData has disallowed "section" attribute"
 // PNaCl is Q_CC_CLANG. TODO: should it not set Q_OF_ELF?
diff --git a/src/corelib/thread/qmutex.cpp b/src/corelib/thread/qmutex.cpp
index 14dc72a1f517356ed5cb816d71b51ad1bfc5c532..6e93e3b0d73f32160580de305d48f7f49e56e594 100644
--- a/src/corelib/thread/qmutex.cpp
+++ b/src/corelib/thread/qmutex.cpp
@@ -271,6 +271,12 @@ bool QMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
 */
 void QMutex::unlock() Q_DECL_NOTHROW
 {
+    // TODO replace this with using pthread stubs instead
+    // Note: Defining QT_NO_THREAD won't work because too many
+    // Qt internals (QFuture, QThreadPool, etc.) depend on threads
+#if defined(Q_OS_NACL_EMSCRIPTEN) && !defined(Q_OS_NACL_EMSCRIPTEN_PTHREADS)
+    return;
+#endif
     QMutexData *current;
     if (fastTryUnlock(current))
         return;
diff --git a/src/corelib/thread/qmutex_unix.cpp b/src/corelib/thread/qmutex_unix.cpp
index 74e0d68f94b6fc93565087f94b9129e179139a98..abb02d77ae50cf5bf1f277fe64b8e006e8da82ce 100644
--- a/src/corelib/thread/qmutex_unix.cpp
+++ b/src/corelib/thread/qmutex_unix.cpp
@@ -70,6 +70,10 @@ QMutexPrivate::~QMutexPrivate()
 
 bool QMutexPrivate::wait(int timeout)
 {
+    // TODO Emscripten specialization could perhaps be replaced with pthread stubs
+#if defined(Q_OS_NACL_EMSCRIPTEN) && !defined(Q_OS_NACL_EMSCRIPTEN_PTHREADS)
+    return false;
+#endif
     report_error(pthread_mutex_lock(&mutex), "QMutex::lock", "mutex lock");
     int errorCode = 0;
     while (!wakeup) {
diff --git a/src/corelib/thread/qwaitcondition_unix.cpp b/src/corelib/thread/qwaitcondition_unix.cpp
index b531707dd2de1367a31364ab414f1bf1f8fd0a07..19c816475e484408875e85a53d4845ccde46411b 100644
--- a/src/corelib/thread/qwaitcondition_unix.cpp
+++ b/src/corelib/thread/qwaitcondition_unix.cpp
@@ -114,6 +114,10 @@ public:
 
     int wait_relative(unsigned long time)
     {
+        // TODO Emscripten specialization could perhaps be replaced with pthread stubs
+#if defined(Q_OS_NACL_EMSCRIPTEN) && !defined(Q_OS_NACL_EMSCRIPTEN_PTHREADS)
+        return 0;
+#endif
         timespec ti;
 #ifdef Q_OS_ANDROID
         if (local_cond_timedwait_relative) {
@@ -128,6 +132,10 @@ public:
 
     bool wait(unsigned long time)
     {
+        // TODO Emscripten specialization could perhaps be replaced with pthread stubs
+#if defined(Q_OS_NACL_EMSCRIPTEN) && !defined(Q_OS_NACL_EMSCRIPTEN_PTHREADS)
+        return true;
+#endif
         int code;
         forever {
             if (time != ULONG_MAX) {
@@ -194,6 +202,11 @@ void QWaitCondition::wakeAll()
 
 bool QWaitCondition::wait(QMutex *mutex, unsigned long time)
 {
+    // TODO Emscripten specialization could perhaps be replaced with pthread stubs
+#if defined(Q_OS_NACL_EMSCRIPTEN) && !defined(Q_OS_NACL_EMSCRIPTEN_PTHREADS)
+    return true;
+#endif
+
     if (! mutex)
         return false;
     if (mutex->isRecursive()) {
@@ -214,6 +227,10 @@ bool QWaitCondition::wait(QMutex *mutex, unsigned long time)
 
 bool QWaitCondition::wait(QReadWriteLock *readWriteLock, unsigned long time)
 {
+    // TODO Emscripten specialization could perhaps be replaced with pthread stubs
+#if defined(Q_OS_NACL_EMSCRIPTEN) && !defined(Q_OS_NACL_EMSCRIPTEN_PTHREADS)
+    return true;
+#endif
     if (!readWriteLock || readWriteLock->d->accessCount == 0)
         return false;
     if (readWriteLock->d->accessCount < -1) {
diff --git a/src/corelib/tools/qsimd.cpp b/src/corelib/tools/qsimd.cpp
index 971d0c0ceca3faf13bd783598d236e77a896881d..137988ce39dbe3122c41046da0cac7864d63e118 100644
--- a/src/corelib/tools/qsimd.cpp
+++ b/src/corelib/tools/qsimd.cpp
@@ -640,6 +640,10 @@ int ffsll(quint64 i)
 # define ffsll __builtin_ffsll
 #endif
 
+#if defined(Q_OS_NACL_NEWLIB) || defined(Q_OS_NACL_EMSCRIPTEN)
+#  define ffsll __builtin_ffsll
+#endif
+
 #ifdef Q_ATOMIC_INT64_IS_SUPPORTED
 Q_CORE_EXPORT QBasicAtomicInteger<quint64> qt_cpu_features[1] = { Q_BASIC_ATOMIC_INITIALIZER(0) };
 #else
diff --git a/src/gui/opengl/qopenglfunctions.h b/src/gui/opengl/qopenglfunctions.h
index ca002642d84cdd241a5a5b5c1fddeae4b3e1ad92..6699b38b3975423d8f0d49dae7be101d51200c2b 100644
--- a/src/gui/opengl/qopenglfunctions.h
+++ b/src/gui/opengl/qopenglfunctions.h
@@ -565,7 +565,7 @@ struct QOpenGLFunctionsPrivate
 
 // GLES2 + OpenGL1 common subset
 
-#ifdef Q_OS_NACL
+#if defined(Q_OS_NACL) && !defined(Q_OS_NACL_EMSCRIPTEN)
 #include "qopenglfunctions_nacl.h"
 #else
 inline void QOpenGLFunctions::glBindTexture(GLenum target, GLuint texture)
diff --git a/src/gui/opengl/qopengltexturehelper.cpp b/src/gui/opengl/qopengltexturehelper.cpp
index 1ef0b3ef17159712e0952e8735f6f001fa3925db..6089a3ed2729d6788fb12e48796eb6e5a01067b4 100644
--- a/src/gui/opengl/qopengltexturehelper.cpp
+++ b/src/gui/opengl/qopengltexturehelper.cpp
@@ -167,6 +167,62 @@ QOpenGLTextureHelper::QOpenGLTextureHelper(QOpenGLContext *context)
     TexSubImage2D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLint , GLint , GLint , GLsizei , GLsizei , GLenum , GLenum , const GLvoid *)>(GetProcAddress(handle, QByteArrayLiteral("glTexSubImage2D")));
     TexSubImage1D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLint , GLint , GLsizei , GLenum , GLenum , const GLvoid *)>(GetProcAddress(handle, QByteArrayLiteral("glTexSubImage1D")));
 
+#elif defined(Q_OS_NACL_EMSCRIPTEN)
+    GetIntegerv = ::glGetIntegerv;
+    GetBooleanv = ::glGetBooleanv;
+    PixelStorei = ::glPixelStorei;
+    GetTexLevelParameteriv = 0;
+    GetTexLevelParameterfv = 0;
+    GetTexParameteriv = ::glGetTexParameteriv;
+    GetTexParameterfv = ::glGetTexParameterfv;
+    GetTexImage = 0;
+    TexImage2D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLint , GLint , GLsizei , GLsizei , GLint , GLenum , GLenum , const GLvoid *)>(::glTexImage2D);
+    TexImage1D = 0;
+    TexParameteriv = ::glTexParameteriv;
+    TexParameteri = ::glTexParameteri;
+    TexParameterfv = ::glTexParameterfv;
+    TexParameterf = ::glTexParameterf;
+
+    // OpenGL 1.1
+    GenTextures = ::glGenTextures;
+    DeleteTextures = ::glDeleteTextures;
+    BindTexture = ::glBindTexture;
+    TexSubImage2D = ::glTexSubImage2D;
+    TexSubImage1D = 0;
+
+    // OpenGL 1.3
+    GetCompressedTexImage = 0;
+    CompressedTexSubImage1D = 0;
+    CompressedTexSubImage2D = ::glCompressedTexSubImage2D;
+    CompressedTexImage1D = 0;
+    CompressedTexImage2D = ::glCompressedTexImage2D;
+    ActiveTexture = ::glActiveTexture;
+
+    // OpenGL 3.0
+    GenerateMipmap = ::glGenerateMipmap;
+
+    // OpenGL 3.2
+    TexImage3DMultisample = 0;
+    TexImage2DMultisample = 0;
+
+    // OpenGL 4.2
+    QOpenGLContext *ctx = QOpenGLContext::currentContext();
+    if (ctx->format().majorVersion() >= 3) {
+        // OpenGL ES 3.0+ has immutable storage for 2D and 3D at least.
+        QOpenGLES3Helper *es3 = static_cast<QOpenGLExtensions *>(ctx->functions())->gles3Helper();
+        TexStorage3D = es3->TexStorage3D;
+        TexStorage2D = es3->TexStorage2D;
+    } else {
+        TexStorage3D = 0;
+        TexStorage2D = 0;
+    }
+    TexStorage1D = 0;
+
+    // OpenGL 4.3
+    TexStorage3DMultisample = 0;
+    TexStorage2DMultisample = 0;
+    TexBufferRange = 0;
+    TextureView = 0;
 #elif defined(QT_OPENGL_ES_2)
     // Here we are targeting OpenGL ES 2.0+ only. This is likely using EGL, where,
     // similarly to WGL, non-extension functions (i.e. any function that is part of the
diff --git a/src/plugins/platforms/pepper/3rdparty/pepper.js/audio.js b/src/plugins/platforms/pepper/3rdparty/pepper.js/audio.js
index 96b21e99f3e3c73891f80ac5bb0d799dbfb2073c..5538088ecad79f4ebc0f67933e3367b268f861f1 100644
--- a/src/plugins/platforms/pepper/3rdparty/pepper.js/audio.js
+++ b/src/plugins/platforms/pepper/3rdparty/pepper.js/audio.js
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+var ENVIRONMENT_IS_PTHREAD; // is set to true in pthread-main.js if we are in a worker
+if(!ENVIRONMENT_IS_PTHREAD) { 
+
 (function() {
 
   var isAudioSupported = function() {
@@ -186,3 +189,5 @@
   ], isAudioSupported);
 
 })();
+
+}
diff --git a/src/plugins/platforms/pepper/3rdparty/pepper.js/base.js b/src/plugins/platforms/pepper/3rdparty/pepper.js/base.js
index 4da0eb52d0bbc51fe3a2ce92b9443e6aad44bad1..e8b2049e6a82f3e9bd2847dcf4a5f3cd87b7201a 100644
--- a/src/plugins/platforms/pepper/3rdparty/pepper.js/base.js
+++ b/src/plugins/platforms/pepper/3rdparty/pepper.js/base.js
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+var ENVIRONMENT_IS_PTHREAD; // is set to true in pthread-main.js if we are in a worker
+if(!ENVIRONMENT_IS_PTHREAD) { 
+
 (function() {
   var DoLog = function(level, value) {
     // TODO enum?
@@ -111,6 +114,65 @@
     Messaging_PostMessage
   ]);
 
+  registerInterface("PPB_Messaging;1.2", [
+    Messaging_PostMessage
+  ]);
+
+  var MessageLoop_InstanceHandle = function(instance) {
+    // console.warn("MessageLoop_InstanceHandle is not implemented")
+    console.log("Returning MessageLoop instance")
+    return 1;
+  };
+
+  var MessageLoop_GetForMainThread = function() {
+    // console.warn("MessageLoop_GetForMainThread is not implemented")
+    console.log("Returning MessageLoop instance main")
+    return 1;
+  };
+
+  var MessageLoop_GetCurrent = function() {
+    // console.warn("MessageLoop_GetCurrent is not implemented")
+    console.log("Returning MessageLoop instance current")
+    return 1;
+  };
+
+  var MessageLoop_Run = function(resource) {
+    // console.warn("MessageLoop_Run is not implemented " + resource)
+    console.log("MessageLoop_Run called")
+    return 1;
+  };
+
+  var MessageLoop_AttachToCurrentThread = function(resource) {
+    // console.warn("MessageLoop_Run is not implemented " + resource)
+    console.log("Attach!")
+    return 1;
+  };
+
+  var MessageLoop_PostWork = function(resource, callback, delay_ms) {
+    // console.log("MessageLoop_PostWork received")
+    var c = glue.getCompletionCallback(callback);
+    Module.requestAnimationFrame(function() {
+      // console.log("MessageLoop_PostWork callback being called!")
+      c(0);
+    });
+    return 0;
+  };
+
+  var MessageLoop_PostQuit = function(resource, should_destroy) {
+    console.warn("MessageLoop_PostQuit is not implemented " + resource)
+    return 0;
+  };
+
+  registerInterface("PPB_MessageLoop;1.0", [
+    MessageLoop_InstanceHandle,
+    MessageLoop_GetForMainThread,
+    MessageLoop_GetCurrent,
+    MessageLoop_AttachToCurrentThread,
+    MessageLoop_Run,
+    MessageLoop_PostWork,
+    MessageLoop_PostQuit,
+  ]);
+
   var Var_AddRef = function(v) {
     if (glue.isRefCountedVarType(glue.getVarType(v))) {
       resources.addRef(glue.getVarUID(v));
@@ -407,3 +469,5 @@
   ]);
 
 })();
+
+}
diff --git a/src/plugins/platforms/pepper/3rdparty/pepper.js/common.js b/src/plugins/platforms/pepper/3rdparty/pepper.js/common.js
new file mode 100644
index 0000000000000000000000000000000000000000..caf4f54733d0e8ee21ca7898891eae5c0f88a220
--- /dev/null
+++ b/src/plugins/platforms/pepper/3rdparty/pepper.js/common.js
@@ -0,0 +1,415 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Javascript module pattern:
+//   see http://en.wikipedia.org/wiki/Unobtrusive_JavaScript#Namespaces
+// In essence, we define an anonymous function which is immediately called and
+// returns a new object. The new object contains only the exported definitions;
+// all other definitions in the anonymous function are inaccessible to external
+// code.
+var common = (function () {
+
+  var addListener = function(elt, event_name, callback) {
+    if (elt.addEventListener) {
+      elt.addEventListener(event_name, callback, false);
+    } else {
+      elt.attachEvent('on' + event_name, callback);
+    }
+  };
+
+  var getImageDataBuffer = function(imageData) {
+    var buffer = imageData.data.buffer;
+    // IE support
+    if(buffer === undefined) {
+      buffer = new ArrayBuffer(imageData.data.length);
+      view = new Uint8Array(buffer);
+      for (var i = 0; i < imageData.data.length; i++) {
+        view[i] = imageData.data[i];
+      }
+    }
+    return buffer;
+  }
+
+  nacl.createInstance = function(config) {
+    var variant = config.module;
+    var e;
+    var width = config.width;
+    var height = config.height;
+    var type = variant.type;
+    if (type == "pnacl") {
+      e = nacl.createEmbedInstance(variant.url, nacl.pnaclMimeType, width, height);
+    } else if (type == "emscripten") {
+      e = nacl.createEmscriptenInstance(variant.url, width, height);
+    } else if (type == "nacl") {
+      e = nacl.createEmbedInstance(variant.url, nacl.naclMimeType, width, height);
+    } else if (type == "host") {
+      e = nacl.createEmbedInstance("bogusURL", nacl.mimetype, width, height);
+    } else {
+      throw new Error("Unknown variant type " + type);
+    }
+    if (config.init) {
+      config.init(e);
+    }
+    if (config.progress) {
+      e.addEventListener('progress', config.progress);
+    }
+    if (config.load) {
+      e.addEventListener('load', config.load);
+    }
+    if (config.error) {
+      e.addEventListener('error', config.error);
+    }
+    config.insert.appendChild(e);
+    e.load();
+    return e;
+  };
+
+  var loadStart;
+
+  /**
+   * Create the Native Client <embed> element as a child of the DOM element
+   * named "listener".
+   *
+   * @param {string} name The name of the example.
+   * @param {string} tool The name of the toolchain, e.g. "glibc", "newlib" etc.
+   * @param {string} path Directory name where .nmf file can be found.
+   * @param {number} width The width to create the plugin.
+   * @param {number} height The height to create the plugin.
+   * @param {Object} optional dictionary of args to send to DidCreateInstance
+   */
+  function createNaClModule(name, tool, path, width, height, args) {
+    loadStart = new Date();
+
+    var isRelease = path.toLowerCase().indexOf('release') != -1;
+
+    var progress = document.createElement('progress');
+    progress.style.width = '480px';
+
+    var modules = {
+        "pnacl": {
+          type: "pnacl",
+          url: path + '/' + name + '.nmf',
+        },
+        "nacl": {
+          type: "nacl",
+          url: path + '/' + name + '.nmf',
+        },
+        "emscripten": {
+          type: "emscripten",
+          url: path + '/' + name + '.js',
+        },
+        "host": {
+          type: "host",
+          mimetype: 'application/x-ppapi-' + (isRelease ? 'release' : 'debug'),
+        }
+    };
+
+    var moduleEl = nacl.createInstance({
+      module: modules[tool],
+      width: width,
+      height: height,
+      insert: document.getElementById('listener'),
+      init: function(e) {
+        e.setAttribute('name', 'nacl_module');
+        e.setAttribute('id', 'nacl_module');
+        e.setAttribute('path', path);
+        // Add any optional arguments
+        if (args) {
+          for (var key in args) {
+            e.setAttribute(key, args[key])
+          }
+        }
+      },
+      progress: function(evt) {
+        var loadPercent = -1.0;
+        progress.max = 100;
+        if (evt.lengthComputable && evt.total > 0) {
+          loadPercent = evt.loaded / evt.total * 100.0;
+        }
+        progress.value = loadPercent;
+      },
+      load: function(evt) {
+        progress.value = 100;
+        document.getElementById('listener').removeChild(progress);
+      },
+      error: function(evt) {
+        progress.value = 100;
+        document.getElementById('listener').removeChild(progress);
+      },
+    });
+
+    if (tool == 'pnacl' && !nacl.hasPNaCl()) {
+      updateStatus('PNaCl requires Chrome 31 or newer.');
+    } else if (tool == 'nacl' && !nacl.hasNaCl()) {
+      updateStatus('NaCl requires Chrome.');
+    } else {
+      document.getElementById('listener').appendChild(progress);
+    }
+
+    // Host plugins don't send a moduleDidLoad message. We'll fake it
+    // here.
+    var isHost = tool == 'win' || tool == 'linux' || tool == 'mac' || tool == 'host';
+    if (isHost) {
+      window.setTimeout(function () {
+        var evt = document.createEvent('Event');
+        evt.initEvent('load', true, true);  // bubbles, cancelable
+        moduleEl.dispatchEvent(evt);
+      }, 100);  // 100 ms
+    }
+    return moduleEl;
+  }
+
+  /**
+   * Add the default "load" and "message" event listeners to the element with
+   * id "listener".
+   *
+   * The "load" event is sent when the module is successfully loaded. The
+   * "message" event is sent when the naclModule posts a message using
+   * PPB_Messaging.PostMessage() (in C) or pp::Instance().PostMessage() (in
+   * C++).
+   */
+  function attachDefaultListeners() {
+    var listenerDiv = document.getElementById('listener');
+    listenerDiv.addEventListener('load', moduleDidLoad, true);
+    listenerDiv.addEventListener('message', handleMessage, true);
+    listenerDiv.addEventListener('crash', handleCrash, true);
+    if (typeof window.attachListeners !== 'undefined') {
+      window.attachListeners();
+    }
+  }
+
+
+  /**
+   * Called when the Browser can not communicate with the Module
+   *
+   * This event listener is registered in attachDefaultListeners above.
+   */
+  function handleCrash(event) {
+    updateStatus('module crashed')
+    if (typeof window.handleCrash !== 'undefined') {
+      window.handleCrash(common.naclModule.lastError);
+    }
+  }
+
+  /**
+   * Called when the NaCl module is loaded.
+   *
+   * This event listener is registered in attachDefaultListeners above.
+   */
+  function moduleDidLoad() {
+    common.naclModule = document.getElementById('nacl_module');
+    updateStatus('loaded');
+    console.log("Create instance: " + (new Date()-loadStart) + " ms");
+
+    if (typeof window.moduleDidLoad !== 'undefined') {
+      window.moduleDidLoad();
+    }
+  }
+
+  /**
+   * Hide the NaCl module's embed element.
+   *
+   * We don't want to hide by default; if we do, it is harder to determine that
+   * a plugin failed to load. Instead, call this function inside the example's
+   * "moduleDidLoad" function.
+   *
+   */
+  function hideModule() {
+    // Setting common.naclModule.style.display = "None" doesn't work; the
+    // module will no longer be able to receive postMessages.
+    common.naclModule.style.height = "0";
+  }
+
+  /**
+   * Return true when |s| starts with the string |prefix|.
+   *
+   * @param {string} s The string to search.
+   * @param {string} prefix The prefix to search for in |s|.
+   */
+  function startsWith(s, prefix) {
+    // indexOf would search the entire string, lastIndexOf(p, 0) only checks at
+    // the first index. See: http://stackoverflow.com/a/4579228
+    return s.lastIndexOf(prefix, 0) === 0;
+  }
+
+  /** Maximum length of logMessageArray. */
+  var kMaxLogMessageLength = 20;
+
+  /** An array of messages to display in the element with id "log". */
+  var logMessageArray = [];
+
+  /**
+   * Add a message to an element with id "log".
+   *
+   * This function is used by the default "log:" message handler.
+   *
+   * @param {string} message The message to log.
+   */
+  function logMessage(message) {
+    logMessageArray.push(message);
+    if (logMessageArray.length > kMaxLogMessageLength)
+      logMessageArray.shift();
+
+    document.getElementById('log').textContent = logMessageArray.join('');
+    console.log(message)
+  }
+
+  /**
+   */
+  var defaultMessageTypes = {
+    'alert': alert,
+    'log': logMessage
+  };
+
+  /**
+   * Called when the NaCl module sends a message to JavaScript (via
+   * PPB_Messaging.PostMessage())
+   *
+   * This event listener is registered in createNaClModule above.
+   *
+   * @param {Event} message_event A message event. message_event.data contains
+   *     the data sent from the NaCl module.
+   */
+  function handleMessage(message_event) {
+    if (typeof message_event.data === 'string') {
+      for (var type in defaultMessageTypes) {
+        if (defaultMessageTypes.hasOwnProperty(type)) {
+          if (startsWith(message_event.data, type + ':')) {
+            func = defaultMessageTypes[type];
+            func(message_event.data.slice(type.length + 1));
+            return;
+          }
+        }
+      }
+    }
+
+    if (typeof window.handleMessage !== 'undefined') {
+      window.handleMessage(message_event);
+    }
+  }
+
+  /**
+   * Called when the DOM content has loaded; i.e. the page's document is fully
+   * parsed. At this point, we can safely query any elements in the document via
+   * document.querySelector, document.getElementById, etc.
+   *
+   * @param {string} name The name of the example.
+   * @param {string} tool The name of the toolchain, e.g. "glibc", "newlib" etc.
+   * @param {string} path Directory name where .nmf file can be found.
+   * @param {number} width The width to create the plugin.
+   * @param {number} height The height to create the plugin.
+   */
+  function domContentLoaded(name, tool, path, width, height) {
+    // If the page loads before the Native Client module loads, then set the
+    // status message indicating that the module is still loading.  Otherwise,
+    // do not change the status message.
+    updateStatus('page loaded');
+    if (common.naclModule == null) {
+      updateStatus('creating ' + tool + ' embed')
+
+      // We use a non-zero sized embed to give Chrome space to place the bad
+      // plug-in graphic, if there is a problem.
+      width = typeof width !== 'undefined' ? width : 200;
+      height = typeof height !== 'undefined' ? height : 200;
+      attachDefaultListeners();
+      createNaClModule(name, tool, path, width, height);
+    } else {
+      // It's possible that the Native Client module onload event fired
+      // before the page's onload event.  In this case, the status message
+      // will reflect 'SUCCESS', but won't be displayed.  This call will
+      // display the current message.
+      updateStatus('waiting');
+    }
+  }
+
+  /** Saved text to display in the element with id 'statusField'. */
+  var statusText = 'NO-STATUSES';
+
+  /**
+   * Set the global status message. If the element with id 'statusField'
+   * exists, then set its HTML to the status message as well.
+   *
+   * @param {string} opt_message The message to set. If null or undefined, then
+   *     set element 'statusField' to the message from the last call to
+   *     updateStatus.
+   */
+  function updateStatus(opt_message) {
+    if (opt_message) {
+      statusText = opt_message;
+    }
+    var statusField = document.getElementById('statusField');
+    if (statusField) {
+      statusField.innerHTML = statusText;
+    }
+  }
+
+  // The symbols to export.
+  return {
+    /** A reference to the NaCl module, once it is loaded. */
+    naclModule: null,
+
+    addListener: addListener,
+    getImageDataBuffer: getImageDataBuffer,
+    attachDefaultListeners: attachDefaultListeners,
+    domContentLoaded: domContentLoaded,
+    createNaClModule: createNaClModule,
+    hideModule: hideModule,
+    logMessage: logMessage,
+    updateStatus: updateStatus
+  };
+
+}());
+
+// Listen for the DOM content to be loaded. This event is fired when parsing of
+// the page's document has finished.
+//common.addListener(document, 'DOMContentLoaded', function() {
+window.onload = function() {
+  var body = document.querySelector('body');
+
+  var loadFunction = common.domContentLoaded;
+  // The data-* attributes on the body can be referenced via body.dataset.
+  if (body.dataset && body.dataset.customLoad && typeof window.domContentLoaded !== 'undefined') {
+    loadFunction = window.domContentLoaded;
+  }
+
+  // From https://developer.mozilla.org/en-US/docs/DOM/window.location
+  var searchVars = {};
+  if (window.location.search.length > 1) {
+    var pairs = window.location.search.substr(1).split("&");
+    for (var key_ix = 0; key_ix < pairs.length; key_ix++) {
+      var keyValue = pairs[key_ix].split("=");
+      searchVars[unescape(keyValue[0])] =
+        keyValue.length > 1 ? unescape(keyValue[1]) : "";
+    }
+  }
+  if (loadFunction) {
+    var name = body.getAttribute("data-name");
+    var tc = body.getAttribute("data-tc");
+    var path = body.getAttribute("data-path");
+    var width = body.getAttribute("data-width") || undefined;
+    var height = body.getAttribute("data-height") || undefined;
+
+    var toolchains = (body.getAttribute("data-tools") || tc || "emscripten newlib pnacl").split(' ');
+    var configs = (body.getAttribute("data-configs") || "Debug Release").split(' ');
+
+    var tc = toolchains.indexOf(searchVars.tc) !== -1 ?
+        searchVars.tc : toolchains[0];
+    var config = configs.indexOf(searchVars.config) !== -1 ?
+      searchVars.config : configs[0];
+    path = path.replace('{tc}', tc).replace('{config}', config);
+
+    // The SDK uses the pnacl toolchain to compile nexes in Debug mode.
+    if (tc == 'pnacl' && config == 'Debug') {
+      tc = 'nacl';
+    }
+    if (tc == 'newlib') {
+      tc = 'nacl';
+    }
+    if (tc == 'win' || tc == 'linux' || tc == 'mac') {
+      tc = 'host';
+    }
+    loadFunction(name, tc, path, width, height);
+  }
+};
+//});
diff --git a/src/plugins/platforms/pepper/3rdparty/pepper.js/file.js b/src/plugins/platforms/pepper/3rdparty/pepper.js/file.js
index ceb66652aca6be95464907ad29ecb28cb46e9f38..c9faa1a5f4af905550cb50c4a023c9de88e30081 100644
--- a/src/plugins/platforms/pepper/3rdparty/pepper.js/file.js
+++ b/src/plugins/platforms/pepper/3rdparty/pepper.js/file.js
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+var ENVIRONMENT_IS_PTHREAD; // is set to true in pthread-main.js if we are in a worker
+if(!ENVIRONMENT_IS_PTHREAD) { 
+
 (function() {
 
   var PP_FILESYSTEMTYPE_INVALID = 0;
@@ -381,3 +384,5 @@
   ]);
 
 })();
+
+}
diff --git a/src/plugins/platforms/pepper/3rdparty/pepper.js/gles.js b/src/plugins/platforms/pepper/3rdparty/pepper.js/gles.js
index 8ae4deeb5e51ec87788c1c04fae5aa74c16defff..f675c10195008e5e78b9d4c0587d59cd474bcf16 100644
--- a/src/plugins/platforms/pepper/3rdparty/pepper.js/gles.js
+++ b/src/plugins/platforms/pepper/3rdparty/pepper.js/gles.js
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+var ENVIRONMENT_IS_PTHREAD; // is set to true in pthread-main.js if we are in a worker
+if(!ENVIRONMENT_IS_PTHREAD) { 
+
 (function() {
 
   // (GLenum) => void
@@ -53,6 +56,10 @@
     if (_context === undefined) {
       return;
     }
+    if(buffer == 0) {
+      // TODO buffer = 0 should release previously bound buffers
+      return;
+    }
     var _buffer = resources.resolve(buffer, BUFFER_RESOURCE);
     if (_buffer === undefined) {
       return;
@@ -67,6 +74,12 @@
     if (_context === undefined) {
       return;
     }
+    if(framebuffer == 0) {
+      // framebuffer = 0 is the default framebuffer provided by the system
+      // and is translated to null in WebGL
+      _context.ctx.bindFramebuffer(target, null);
+      return;
+    }
     var _framebuffer = coerceFramebuffer(framebuffer);
     _context.ctx.bindFramebuffer(target, _framebuffer);
   }
@@ -561,7 +574,48 @@
   }
 
   var OpenGLES2_GetProgramiv = function(context, program, pname, params) {
-    throw "OpenGLES2_GetProgramiv not implemented";
+    if(pname == 35716 || pname == 35720) { 
+      // 35716 = GL_INFO_LOG_LENGTH (0x8B84)
+      // 35720 = GL_SHADER_SOURCE_LENGTH (0x8B88)
+      // These are not allowed in WebGL, but are allowed in GLES.
+      // Return 0 so that legacy code thinks there is no log or source to print
+      var result = 0
+      setValue(params, result, 'i32');
+      return
+    }
+    var _context = resources.resolve(context, GRAPHICS_3D_RESOURCE);
+    if (_context === undefined) {
+      return 0;
+    }
+    var _program = resources.resolve(program, PROGRAM_RESOURCE);
+    if (_program === undefined) {
+      return 0;
+    }
+    var result = _context.ctx.getProgramParameter(_program.native, pname);
+    switch(pname) {
+    case _context.ctx.DELETE_STATUS:
+      setValue(params, result, 'i8');
+      break;
+    case _context.ctx.LINK_STATUS:
+      setValue(params, result, 'i8');
+      break;
+    case _context.ctx.VALIDATE_STATUS:
+      setValue(params, result, 'i8');
+      break;
+    case _context.ctx.ATTACHED_SHADERS:
+      setValue(params, result, 'i32');
+      break;
+    case _context.ctx.ACTIVE_ATTRIBUTES:
+      setValue(params, result, 'i32');
+      break;
+    case _context.ctx.ACTIVE_UNIFORMS:
+      setValue(params, result, 'i32');
+      break;
+    default:
+      console.warn("OpenGLES2_GetProgramiv unknown parameter type, assume i32")
+      setValue(params, result, 'i32');
+      break;
+    }
   }
 
   var OpenGLES2_GetProgramInfoLog = function(context, program, bufsize, length, infolog) {
@@ -573,7 +627,16 @@
   }
 
   var OpenGLES2_GetShaderiv = function(context, shader, pname, params) {
-    throw "OpenGLES2_GetShaderiv not implemented";
+    var _context = resources.resolve(context, GRAPHICS_3D_RESOURCE);
+    if (_context === undefined) {
+      return 0;
+    }
+    var _shader = resources.resolve(shader, SHADER_RESOURCE);
+    if (_shader === undefined) {
+      return 0;
+    }
+    var result = _context.ctx.getShaderParameter(_shader.native, pname);
+    setValue(params, result, 'i32');
   }
 
   var OpenGLES2_GetShaderInfoLog = function(context, shader, bufsize, length, infolog) {
@@ -1091,11 +1154,21 @@
       return;
     }
     var _value = HEAPF32.subarray((value>>2), (value>>2) + 16 * count);
+    // TODO: This is temporary to cast SharedFloat32Array to a Float32Array. Remove this once https://bugzilla.mozilla.org/show_bug.cgi?id=1205390 lands.
+    _value = new Float32Array(_value);
     _context.ctx.uniformMatrix4fv(_location.native, transpose, _value);
   }
   // ppapi (GLuint) => void
   // webgl (WebGLProgram) => void
   var OpenGLES2_UseProgram = function(context, program) {
+    if(program === 0) {
+      // TODO Is useprogram(0) okay to call?
+      // OpenGLES2_UseProgram: Requested to use program 0. "
+      // This is not a defined operation in WebGL, but in legacy
+      // code it is a request to release the program.
+      // Here, nothing will be done when calling glUseProgram(0).
+      return
+    }
     var _context = resources.resolve(context, GRAPHICS_3D_RESOURCE);
     if (_context === undefined) {
       return;
@@ -1386,3 +1459,5 @@
 // UniformMatrix2fv: Cannot deal with overloads
 // UniformMatrix3fv: Cannot deal with overloads
 // 94:48
+
+}
diff --git a/src/plugins/platforms/pepper/3rdparty/pepper.js/gles_ext.js b/src/plugins/platforms/pepper/3rdparty/pepper.js/gles_ext.js
index 9dfc7f4bf36ad44beddb4f4cba8bef61810183d2..f35b6b038de8cdf31037b854bf5a5ebf0f528eff 100644
--- a/src/plugins/platforms/pepper/3rdparty/pepper.js/gles_ext.js
+++ b/src/plugins/platforms/pepper/3rdparty/pepper.js/gles_ext.js
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+var ENVIRONMENT_IS_PTHREAD; // is set to true in pthread-main.js if we are in a worker
+if(!ENVIRONMENT_IS_PTHREAD) { 
+
 (function() {
   // PPB_OpenGLES2InstancedArrays
 
@@ -212,5 +215,30 @@
     OpenGLES2Query_GetQueryivEXT,
     OpenGLES2Query_GetQueryObjectuivEXT,
   ]);
+  
+  var OpenGLES2VertexArrayObject_BindVertexArrayOES = function(context, x, y, width, height) {
+    throw "OpenGLES2_BindVertexArrayOES is not implemented"
+  }
+  var OpenGLES2VertexArrayObject_DeleteVertexArrayOES = function(context, x, y, width, height) {
+    throw "OpenGLES2_BindVertexArrayOES is not implemented"
+  }
+  var OpenGLES2VertexArrayObject_GenVertexArrayOES = function(context, x, y, width, height) {
+    throw "OpenGLES2_BindVertexArrayOES is not implemented"
+  }
+  var OpenGLES2VertexArrayObject_IsVertexArrayOES = function(context, x, y, width, height) {
+    throw "OpenGLES2_BindVertexArrayOES is not implemented"
+  }
+
+  registerInterface("PPB_OpenGLES2VertexArrayObject;1.0", [
+    OpenGLES2VertexArrayObject_BindVertexArrayOES,
+    OpenGLES2VertexArrayObject_DeleteVertexArrayOES,
+    OpenGLES2VertexArrayObject_GenVertexArrayOES,
+    OpenGLES2VertexArrayObject_IsVertexArrayOES
+  ]);
+
+  registerInterface("PPB_OpenGLES2DrawBuffers(Dev);1.0", [
+  ]);
 
 })();
+
+}
diff --git a/src/plugins/platforms/pepper/3rdparty/pepper.js/graphics_2d.js b/src/plugins/platforms/pepper/3rdparty/pepper.js/graphics_2d.js
index e3d5918e042e1168b63db373ae22329b9ebbc3a1..e619eb24442ffcc2ef7f353f14e98532ed512bc7 100644
--- a/src/plugins/platforms/pepper/3rdparty/pepper.js/graphics_2d.js
+++ b/src/plugins/platforms/pepper/3rdparty/pepper.js/graphics_2d.js
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+var ENVIRONMENT_IS_PTHREAD; // is set to true in pthread-main.js if we are in a worker
+if(!ENVIRONMENT_IS_PTHREAD) { 
+
 (function() {
 
   var Graphics2D_Create = function(instance, size_ptr, is_always_opaque) {
@@ -386,3 +389,5 @@
     ImageData_Unmap
   ]);
 })();
+
+}
diff --git a/src/plugins/platforms/pepper/3rdparty/pepper.js/graphics_3d.js b/src/plugins/platforms/pepper/3rdparty/pepper.js/graphics_3d.js
index 42e62d809d662bcffc4c3cf14ca55de54e451593..ffc4c376d4cd93af8a8b623c7f39ef6b467e1296 100644
--- a/src/plugins/platforms/pepper/3rdparty/pepper.js/graphics_3d.js
+++ b/src/plugins/platforms/pepper/3rdparty/pepper.js/graphics_3d.js
@@ -2,9 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+var ENVIRONMENT_IS_PTHREAD; // is set to true in pthread-main.js if we are in a worker
+if(!ENVIRONMENT_IS_PTHREAD) { 
+
 (function() {
 
   var getContext = function(c, params) {
+    // TODO we bypass this with Browser.createContext, except for in the test
     return c.getContext('webgl', params) || c.getContext("experimental-webgl", params);
   };
 
@@ -94,7 +98,7 @@
     return resources.register(GRAPHICS_3D_RESOURCE, {
       canvas: canvas,
       bound: false,
-      ctx: getContext(canvas, {
+      ctx: Browser.createContext(canvas, true, true, {
         "alpha": alpha_size > 0,
         "depth": depth_size > 0,
         "stencil": stencil_size > 0,
@@ -143,8 +147,10 @@
 
   var Graphics3D_SwapBuffers = function(context, callback) {
     // TODO double buffering.
+    // console.log("Graphics3D_SwapBuffers request received")
     var c = glue.getCompletionCallback(callback);
     Module.requestAnimationFrame(function() {
+      // console.log("Graphics3D_SwapBuffers calling back!")
       c(0);
     });
   };
@@ -162,3 +168,5 @@
       return !!getContext(document.createElement("canvas"));
   });
 })();
+
+}
diff --git a/src/plugins/platforms/pepper/3rdparty/pepper.js/input_events.js b/src/plugins/platforms/pepper/3rdparty/pepper.js/input_events.js
index 274212aa1f23164bd77aac44d8b1b3e7f0881326..1ce29bba3c75284b9703c140687b34cf4603723f 100644
--- a/src/plugins/platforms/pepper/3rdparty/pepper.js/input_events.js
+++ b/src/plugins/platforms/pepper/3rdparty/pepper.js/input_events.js
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+var ENVIRONMENT_IS_PTHREAD; // is set to true in pthread-main.js if we are in a worker
+if(!ENVIRONMENT_IS_PTHREAD) { 
+
 (function() {
 
   //Enums copied from ppb_input_event.h
@@ -191,7 +194,8 @@
           movement: GetMovement(event),
           delta: GetWheelScroll(event),
           scrollByPage: event.deltaMode === 2,
-          keyCode: event.keyCode
+          keyCode: event.keyCode,
+          charCode: event.charCode
         });
 
         var rval = _HandleInputEvent(instance, obj_uid);
@@ -426,16 +430,33 @@
     return res.keyCode;
   };
 
+  var KeyboardInputEvent_GetCode = function(event) {
+    throw "KeyboardInputEvent_GetCode not implemented";
+  };
+
   var KeyboardInputEvent_GetCharacterText = function(ptr, event) {
-    // TODO(grosse): Find way to implement this
-    glue.jsToMemoryVar(undefined, ptr);
+    var res = resources.resolve(event, INPUT_EVENT_RESOURCE);
+    if (res === undefined) {
+      return 0;
+    }
+    glue.jsToMemoryVar(String.fromCharCode(res.charCode), ptr);
   };
 
-  registerInterface("PPB_KeyboardInputEvent;1.0", [
-    KeyboardInputEvent_Create,
-    KeyboardInputEvent_IsKeyboardInputEvent,
-    KeyboardInputEvent_GetKeyCode,
-    KeyboardInputEvent_GetCharacterText
-  ]);
+    registerInterface("PPB_KeyboardInputEvent;1.0", [
+      KeyboardInputEvent_Create,
+      KeyboardInputEvent_IsKeyboardInputEvent,
+      KeyboardInputEvent_GetKeyCode,
+      KeyboardInputEvent_GetCharacterText
+    ]);
+
+    registerInterface("PPB_KeyboardInputEvent;1.2", [
+      KeyboardInputEvent_Create,
+      KeyboardInputEvent_IsKeyboardInputEvent,
+      KeyboardInputEvent_GetKeyCode,
+      KeyboardInputEvent_GetCharacterText,
+      KeyboardInputEvent_GetCode,
+    ]);
 
 })();
+
+}
diff --git a/src/plugins/platforms/pepper/3rdparty/pepper.js/mouse_lock.js b/src/plugins/platforms/pepper/3rdparty/pepper.js/mouse_lock.js
index fc25df1cbf0dabfc0cb29ab2a362ae612e9683f6..09442b8bef4e683ab2be4b9da38f291fe9a65095 100644
--- a/src/plugins/platforms/pepper/3rdparty/pepper.js/mouse_lock.js
+++ b/src/plugins/platforms/pepper/3rdparty/pepper.js/mouse_lock.js
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+var ENVIRONMENT_IS_PTHREAD; // is set to true in pthread-main.js if we are in a worker
+if(!ENVIRONMENT_IS_PTHREAD) { 
+
 (function() {
 
   var MouseLock_LockMouse = function(instance, callback) {
@@ -102,3 +105,5 @@
   });
 
 })();
+
+}
diff --git a/src/plugins/platforms/pepper/3rdparty/pepper.js/ppapi_preamble.js b/src/plugins/platforms/pepper/3rdparty/pepper.js/ppapi_preamble.js
index 910eb70b3084b74fc91643393da9e7e4e05cfec0..ea856dfe4160d40009b6eaea1a37c4ebf017b791 100644
--- a/src/plugins/platforms/pepper/3rdparty/pepper.js/ppapi_preamble.js
+++ b/src/plugins/platforms/pepper/3rdparty/pepper.js/ppapi_preamble.js
@@ -5,6 +5,13 @@
 // TODO(ncbray): re-enable once Emscripten stops including code with octal values.
 //"use strict";
 
+var doCreateInstance = doCreateInstance || function () {
+    console.log("Please call QtLoader.load() instead of loading your application script.")
+}
+
+var ENVIRONMENT_IS_PTHREAD; // is set to true in pthread-main.js if we are in a worker
+if(!ENVIRONMENT_IS_PTHREAD) {
+
 var clamp = function(value, min, max) {
   if (value < min) {
     return min;
@@ -96,6 +103,8 @@ var ARRAY_RESOURCE = 21;
 var DICTIONARY_RESOURCE = 22;
 var WEB_SOCKET_RESOURCE = 23;
 
+var MESSAGE_LOOP_RESOURCE = 24;
+
 var ResourceManager = function() {
   this.lut = {};
   this.uid = 1;
@@ -296,10 +305,10 @@ var createInterface = function(name, functions) {
   interfaces[name] = ptr;
 };
 
-var Module = {
-  "noInitialRun": true,
-  "noExitRuntime": true,
-  "preInit": function() {
+var Module = Module || {};
+Module["noInitialRun"] = true;
+Module["noExitRuntime"] = true;
+Module["onRuntimeInitialized"] = function() {
     for (var i = 0; i < declaredInterfaces.length; i++) {
       var inf = declaredInterfaces[i];
       if (inf.supported === undefined || inf.supported()) {
@@ -309,7 +318,7 @@ var Module = {
       }
     }
     declaredInterfaces = [];
-  }
+    doCreateInstance();
 };
 
 var CreateInstance = function(width, height, shadow_instance) {
@@ -983,3 +992,5 @@ var _GetBrowserInterface = function(interface_name) {
   }
   return inf;
 };
+
+}
diff --git a/src/plugins/platforms/pepper/3rdparty/pepper.js/testing.js b/src/plugins/platforms/pepper/3rdparty/pepper.js/testing.js
index 79b4fc08c7764babae6d6bbaa51dd07756b54953..21ef601a5fc9c1961382e614c24c8a17a04e62be 100644
--- a/src/plugins/platforms/pepper/3rdparty/pepper.js/testing.js
+++ b/src/plugins/platforms/pepper/3rdparty/pepper.js/testing.js
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+var ENVIRONMENT_IS_PTHREAD; // is set to true in pthread-main.js if we are in a worker
+if(!ENVIRONMENT_IS_PTHREAD) { 
+
 (function() {
 
   var Testing_Dev_ReadImageData = function(device_context_2d, image, top_left) {
@@ -53,3 +56,5 @@
     Testing_Dev_SetMinimumArrayBufferSizeForShmem,
   ]);
 })();
+
+}
diff --git a/src/plugins/platforms/pepper/3rdparty/pepper.js/url_loader.js b/src/plugins/platforms/pepper/3rdparty/pepper.js/url_loader.js
index af40696cc826f988de29fea82c5610dbefe4cf04..58f532c0540da15ba82fa7573bd73713572508c2 100644
--- a/src/plugins/platforms/pepper/3rdparty/pepper.js/url_loader.js
+++ b/src/plugins/platforms/pepper/3rdparty/pepper.js/url_loader.js
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+var ENVIRONMENT_IS_PTHREAD; // is set to true in pthread-main.js if we are in a worker
+if(!ENVIRONMENT_IS_PTHREAD) { 
+
 (function() {
 
   // Canonicalize the URL using the DOM.
@@ -369,3 +372,5 @@
   ]);
 
 })();
+
+}
diff --git a/src/plugins/platforms/pepper/3rdparty/pepper.js/view.js b/src/plugins/platforms/pepper/3rdparty/pepper.js/view.js
index 423539fc6b5301eb553664280001139a0562b417..8ceec3a321ffa46643f1ea6cdeb6f7870ca1a629 100644
--- a/src/plugins/platforms/pepper/3rdparty/pepper.js/view.js
+++ b/src/plugins/platforms/pepper/3rdparty/pepper.js/view.js
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+var ENVIRONMENT_IS_PTHREAD; // is set to true in pthread-main.js if we are in a worker
+if(!ENVIRONMENT_IS_PTHREAD) { 
+
 (function() {
   var View_IsView = function(resource) {
     return resources.is(resource, VIEW_RESOURCE);
@@ -88,4 +91,17 @@
     View_GetDeviceScale,
     View_GetCSSScale,
   ]);
+
+  registerInterface("PPB_View;1.2", [
+    View_IsView,
+    View_GetRect,
+    View_IsFullscreen,
+    View_IsVisible,
+    View_IsPageVisible,
+    View_GetClipRect,
+    View_GetDeviceScale,
+    View_GetCSSScale,
+  ]);
 })();
+
+}
diff --git a/src/plugins/platforms/pepper/3rdparty/pepper.js/web_socket.js b/src/plugins/platforms/pepper/3rdparty/pepper.js/web_socket.js
index 732efd9f889b68829bb3b9cb9bb32968e91bdca7..8eccb30e5bdd35b5f41049778dbeb48791fbceb4 100644
--- a/src/plugins/platforms/pepper/3rdparty/pepper.js/web_socket.js
+++ b/src/plugins/platforms/pepper/3rdparty/pepper.js/web_socket.js
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+var ENVIRONMENT_IS_PTHREAD; // is set to true in pthread-main.js if we are in a worker
+if(!ENVIRONMENT_IS_PTHREAD) { 
+
 (function() {
 /*
 
@@ -322,3 +325,5 @@ http://creativecommons.org/publicdomain/zero/1.0/legalcode
   ]);
 
 })();
+
+}
diff --git a/src/plugins/platforms/pepper/pepper.js.pri b/src/plugins/platforms/pepper/pepper.js.pri
index 0b3a557f8195dede26a5da64dff70459db9a134b..43472e3a76946128fd6e6156b01719dc16105ce4 100644
--- a/src/plugins/platforms/pepper/pepper.js.pri
+++ b/src/plugins/platforms/pepper/pepper.js.pri
@@ -2,8 +2,12 @@
 PPAPI_CPP_SOURCE= $$(NACL_SDK_ROOT)/src/ppapi_cpp
 SOURCES += \
     $${PPAPI_CPP_SOURCE}/array_output.cc \
-    $${PPAPI_CPP_SOURCE}/audio.cc \
+    $${PPAPI_CPP_SOURCE}/audio_buffer.cc \
     $${PPAPI_CPP_SOURCE}/audio_config.cc \
+    $${PPAPI_CPP_SOURCE}/audio_encoder.cc \
+    $${PPAPI_CPP_SOURCE}/audio.cc \
+    $${PPAPI_CPP_SOURCE}/compositor_layer.cc \
+    $${PPAPI_CPP_SOURCE}/compositor.cc \
     $${PPAPI_CPP_SOURCE}/core.cc \
     $${PPAPI_CPP_SOURCE}/cursor_control_dev.cc \
     $${PPAPI_CPP_SOURCE}/directory_entry.cc \
@@ -14,14 +18,16 @@ SOURCES += \
     $${PPAPI_CPP_SOURCE}/font_dev.cc \
     $${PPAPI_CPP_SOURCE}/fullscreen.cc \
     $${PPAPI_CPP_SOURCE}/graphics_2d.cc \
-    $${PPAPI_CPP_SOURCE}/graphics_3d.cc \
     $${PPAPI_CPP_SOURCE}/graphics_3d_client.cc \
+    $${PPAPI_CPP_SOURCE}/graphics_3d.cc \
     $${PPAPI_CPP_SOURCE}/host_resolver.cc \
     $${PPAPI_CPP_SOURCE}/image_data.cc \
     $${PPAPI_CPP_SOURCE}/input_event.cc \
-    $${PPAPI_CPP_SOURCE}/instance.cc \
     $${PPAPI_CPP_SOURCE}/instance_handle.cc \
+    $${PPAPI_CPP_SOURCE}/instance.cc \
     $${PPAPI_CPP_SOURCE}/lock.cc \
+    $${PPAPI_CPP_SOURCE}/media_stream_audio_track.cc \
+    $${PPAPI_CPP_SOURCE}/media_stream_video_track.cc \
     $${PPAPI_CPP_SOURCE}/memory_dev.cc \
     $${PPAPI_CPP_SOURCE}/message_loop.cc \
     $${PPAPI_CPP_SOURCE}/module.cc \
@@ -38,7 +44,6 @@ SOURCES += \
     $${PPAPI_CPP_SOURCE}/rect.cc \
     $${PPAPI_CPP_SOURCE}/resource.cc \
     $${PPAPI_CPP_SOURCE}/scriptable_object_deprecated.cc \
-    $${PPAPI_CPP_SOURCE}/selection_dev.cc \
     $${PPAPI_CPP_SOURCE}/simple_thread.cc \
     $${PPAPI_CPP_SOURCE}/tcp_socket.cc \
     $${PPAPI_CPP_SOURCE}/text_input_controller.cc \
@@ -47,17 +52,18 @@ SOURCES += \
     $${PPAPI_CPP_SOURCE}/url_loader.cc \
     $${PPAPI_CPP_SOURCE}/url_request_info.cc \
     $${PPAPI_CPP_SOURCE}/url_response_info.cc \
-    $${PPAPI_CPP_SOURCE}/var.cc \
-    $${PPAPI_CPP_SOURCE}/var_array.cc \
     $${PPAPI_CPP_SOURCE}/var_array_buffer.cc \
+    $${PPAPI_CPP_SOURCE}/var_array.cc \
     $${PPAPI_CPP_SOURCE}/var_dictionary.cc \
-    $${PPAPI_CPP_SOURCE}/view.cc \
+    $${PPAPI_CPP_SOURCE}/var.cc \
+    $${PPAPI_CPP_SOURCE}/video_decoder.cc \
+    $${PPAPI_CPP_SOURCE}/video_encoder.cc \
+    $${PPAPI_CPP_SOURCE}/video_frame.cc \
     $${PPAPI_CPP_SOURCE}/view_dev.cc \
-    $${PPAPI_CPP_SOURCE}/websocket.cc \
+    $${PPAPI_CPP_SOURCE}/view.cc \
     $${PPAPI_CPP_SOURCE}/websocket_api.cc \
-    $${PPAPI_CPP_SOURCE}/zoom_dev.cc \
-    $${PPAPI_CPP_SOURCE}/media_stream_video_track.cc \
-    $${PPAPI_CPP_SOURCE}/video_frame.cc \
+    $${PPAPI_CPP_SOURCE}/websocket.cc
+
 
 # libppapi (stub)
 SOURCES += \
@@ -65,8 +71,32 @@ SOURCES += \
     # rest of libppapi is implemented in JavasScript and added to the build
     # with '--pre-js' at link time.
 
+!emscripten {
+# Emscripten provides a better GLES2 API directly than pepper.js
+# TODO Consider adding back this when pepper.js has better GLES2 support
 # OpenGL
 PPAPI_GLES_SOURCE= $$(NACL_SDK_ROOT)/src/ppapi_gles2
 SOURCES +=\
     $${PPAPI_GLES_SOURCE}/gles2.c \
     $${PPAPI_GLES_SOURCE}/gl2ext_ppapi.c \
+
+}
+
+OTHER_FILES += \
+    $$PWD/3rdparty/pepper.js/audio.js \
+    $$PWD/3rdparty/pepper.js/base.js \
+    $$PWD/3rdparty/pepper.js/common.js \
+    $$PWD/3rdparty/pepper.js/file.js \
+    $$PWD/3rdparty/pepper.js/gles.js \
+    $$PWD/3rdparty/pepper.js/gles_ext.js \
+    $$PWD/3rdparty/pepper.js/graphics_2d.js \
+    $$PWD/3rdparty/pepper.js/graphics_3d.js \
+    $$PWD/3rdparty/pepper.js/input_events.js \
+    $$PWD/3rdparty/pepper.js/loadnacl.js \
+    $$PWD/3rdparty/pepper.js/mouse_lock.js \
+    $$PWD/3rdparty/pepper.js/ppapi_preamble.js \
+    $$PWD/3rdparty/pepper.js/testing.js \
+    $$PWD/3rdparty/pepper.js/url_loader.js \
+    $$PWD/3rdparty/pepper.js/view.js \
+    $$PWD/3rdparty/pepper.js/web_socket.js \
+    $$PWD/3rdparty/pepper.js/LICENSE
diff --git a/src/plugins/platforms/pepper/qpeppereventdispatcher.cpp b/src/plugins/platforms/pepper/qpeppereventdispatcher.cpp
index c5549c75f7ade427f93d0c076ef69749e3fcd7bc..7af6b2d1618ccdf895e455ca939f382bf7182454 100644
--- a/src/plugins/platforms/pepper/qpeppereventdispatcher.cpp
+++ b/src/plugins/platforms/pepper/qpeppereventdispatcher.cpp
@@ -45,6 +45,7 @@ QPepperEventDispatcher::QPepperEventDispatcher(QObject *parent)
     , m_currentTimerSerial(0)
     , m_messageLoop(pp::MessageLoop::GetCurrent())
     , m_completionCallbackFactory(this)
+    , m_hasPendingProcessEvents(false)
 {
     qCDebug(QT_PLATFORM_PEPPER_EVENTDISPATHCER) << "QPepperEventDispatcher()";
 }
@@ -54,9 +55,25 @@ QPepperEventDispatcher::~QPepperEventDispatcher() {}
 bool QPepperEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
 {
     bool processed = false;
+#ifdef Q_OS_NACL_EMSCRIPTEN
+    // We need to give the control back to the browser due to lack of PTHREADS
+    // Limit the number of events that may be processed at the time
+    // TODO: Set maxProcessedEvents to the actual number of pending events, the real issue is that events may spawn new events
+    int maxProcessedEvents = 2;
+    int processedCount = 0;
+    do {
+        processed = QUnixEventDispatcherQPA::processEvents(flags);
+        processedCount += 1;
+    } while (processed && hasPendingEvents() && processedCount < maxProcessedEvents);
+    // Schedule a new processing loop if we still have events pending
+    if (hasPendingEvents()) {
+        scheduleProcessEvents();
+    }
+#else
     do {
         processed = QUnixEventDispatcherQPA::processEvents(flags);
     } while (processed && hasPendingEvents());
+#endif
     return true;
 }
 
@@ -193,19 +210,23 @@ void QPepperEventDispatcher::timerCallback(int32_t result, int32_t timerSerial)
 
 void QPepperEventDispatcher::scheduleProcessEvents()
 {
-    qCDebug(QT_PLATFORM_PEPPER_EVENTDISPATHCER) << "scheduleProcessEvents";
-    pp::CompletionCallback processEvents
-        = m_completionCallbackFactory.NewCallback(&QPepperEventDispatcher::processEventsCallback);
-    int32_t result = m_messageLoop.PostWork(processEvents);
-    if (result != PP_OK)
-        qCDebug(QT_PLATFORM_PEPPER_EVENTDISPATHCER) << "scheduleProcessEvents PostWork error"
-                                                    << result;
+    qCDebug(QT_PLATFORM_PEPPER_EVENTDISPATHCER) << "scheduleProcessEvents" << m_hasPendingProcessEvents;
+    if (!m_hasPendingProcessEvents) {
+        m_hasPendingProcessEvents = true;
+        pp::CompletionCallback processEvents
+            = m_completionCallbackFactory.NewCallback(&QPepperEventDispatcher::processEventsCallback);
+        int32_t result = m_messageLoop.PostWork(processEvents);
+        if (result != PP_OK)
+            qCDebug(QT_PLATFORM_PEPPER_EVENTDISPATHCER) << "scheduleProcessEvents PostWork error"
+                                                        << result;
+    }
 }
 
 void QPepperEventDispatcher::processEventsCallback(int32_t status)
 {
     Q_UNUSED(status);
     qCDebug(QT_PLATFORM_PEPPER_EVENTDISPATHCER) << "processEvents";
+    m_hasPendingProcessEvents = false;
 
     processEvents();
 }
diff --git a/src/plugins/platforms/pepper/qpeppereventdispatcher.h b/src/plugins/platforms/pepper/qpeppereventdispatcher.h
index 935f8ac653b830c5a7452b1737002761c4dbdd1a..efcb458b1134ace956ec5cf2ebdf85edd2401966 100644
--- a/src/plugins/platforms/pepper/qpeppereventdispatcher.h
+++ b/src/plugins/platforms/pepper/qpeppereventdispatcher.h
@@ -94,6 +94,7 @@ private:
     QHash<int, PepperTimerInfo> m_timerDetails;
     pp::MessageLoop m_messageLoop;
     pp::CompletionCallbackFactory<QPepperEventDispatcher> m_completionCallbackFactory;
+    bool m_hasPendingProcessEvents;
 };
 
 QT_END_NAMESPACE
diff --git a/src/plugins/platforms/pepper/qpepperfontdatabase.cpp b/src/plugins/platforms/pepper/qpepperfontdatabase.cpp
index e54293958737a1289fa93e0448f435de83cc4c9d..5b816b805986901d105b4b35214f70a7e4ede8b9 100644
--- a/src/plugins/platforms/pepper/qpepperfontdatabase.cpp
+++ b/src/plugins/platforms/pepper/qpepperfontdatabase.cpp
@@ -49,6 +49,8 @@ void QPepperFontDatabase::populateFontDatabase()
     registerFont("Arial", "", QFont::Normal, QFont::StyleNormal, QFont::Unstretched, true, true, 12,
                  writingSystems, 0);
 #else
+    Q_INIT_RESOURCE(naclfonts);
+
     // Load font file from resources. Currently
     // all fonts needs to be bundled with the nexe
     // as Qt resources.
diff --git a/src/plugins/platforms/pepper/qpepperglcontext.cpp b/src/plugins/platforms/pepper/qpepperglcontext.cpp
index 7587062c054b366ef26b858ec6ecd7b524a6a5f1..da7cc315fd929c65742d4d91f57d12f2b6ba0fa4 100644
--- a/src/plugins/platforms/pepper/qpepperglcontext.cpp
+++ b/src/plugins/platforms/pepper/qpepperglcontext.cpp
@@ -44,7 +44,12 @@
 
 #include <ppapi/cpp/graphics_3d.h>
 #include <ppapi/cpp/graphics_3d_client.h>
+
+// Emscriptens GLES2 API is more complete than pepper.js
+#ifndef Q_OS_NACL_EMSCRIPTEN
 #include <ppapi/gles2/gl2ext_ppapi.h>
+#endif
+
 
 #include <QtCore/QCoreApplication>
 #include <QtCore/QThread>
@@ -109,15 +114,18 @@ bool QPepperGLContext::makeCurrent(QPlatformSurface *surface)
         }
         m_currentSize = newSize;
     }
-
+#ifndef Q_OS_NACL_EMSCRIPTEN
     glSetCurrentContextPPAPI(m_context.pp_resource());
+#endif
     return true;
 }
 
 void QPepperGLContext::doneCurrent()
 {
     qCDebug(QT_PLATFORM_PEPPER_GLCONTEXT) << "doneCurrent";
+#ifndef Q_OS_NACL_EMSCRIPTEN
     glSetCurrentContextPPAPI(0);
+#endif
 }
 
 QFunctionPointer QPepperGLContext::getProcAddress(const QByteArray &procName)
@@ -144,10 +152,12 @@ void QPepperGLContext::flushCallback(int32_t)
 bool QPepperGLContext::initGl()
 {
     qCDebug(QT_PLATFORM_PEPPER_GLCONTEXT) << "initGl";
+#ifndef Q_OS_NACL_EMSCRIPTEN
     if (!glInitializePPAPI(pp::Module::Get()->get_browser_interface())) {
         qWarning("Unable to initialize GL PPAPI!\n");
         return false;
     }
+#endif
     m_currentSize = QPepperInstancePrivate::get()->geometry().size();
     QSurfaceFormat f = format();
 
@@ -165,7 +175,9 @@ bool QPepperGLContext::initGl()
     if (!instance->BindGraphics(m_context)) {
         qWarning("Unable to bind 3d context!\n");
         m_context = pp::Graphics3D();
+#ifndef Q_OS_NACL_EMSCRIPTEN
         glSetCurrentContextPPAPI(0);
+#endif
         return false;
     }
 
diff --git a/src/plugins/platforms/pepper/qpepperpluginmain.cpp b/src/plugins/platforms/pepper/qpepperpluginmain.cpp
index 598401e57f7872252252b0f3045a78f9c2f87676..dfc7545626b532ccb56a79f3ecc786833fde1209 100644
--- a/src/plugins/platforms/pepper/qpepperpluginmain.cpp
+++ b/src/plugins/platforms/pepper/qpepperpluginmain.cpp
@@ -33,40 +33,43 @@
 **
 ****************************************************************************/
 
-#include "qpepperintegration.h"
+// TODO: Check if this is needed. Removed because it caused duplicate
+// symbol definition of qt_plugin_query_metadata for everything linking QtGui
 
-#include <QtCore/QDebug>
-#include <qpa/qplatformintegrationplugin.h>
-
-QT_BEGIN_NAMESPACE
-
-class QPepperIntegrationPlugin : public QPlatformIntegrationPlugin
-{
-    Q_OBJECT
-    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.1" FILE
-                          "pepper.json")
-public:
-    QStringList keys() const;
-    QPlatformIntegration *create(const QString &, const QStringList &) Q_DECL_OVERRIDE;
-};
-
-QStringList QPepperIntegrationPlugin::keys() const
-{
-    QStringList list;
-    list << QStringLiteral("pepper");
-    return list;
-}
-
-QPlatformIntegration *QPepperIntegrationPlugin::create(const QString &system,
-                                                       const QStringList &paramList)
-{
-    Q_UNUSED(paramList);
-    // qDebug() << "QPepperIntegrationPlugin::create" << system;
-    if (system.toLower() == QStringLiteral("pepper"))
-        return new QPepperIntegration;
-    return 0;
-}
-
-QT_END_NAMESPACE
-
-#include "qpepperpluginmain.moc"
+// #include "qpepperintegration.h"
+//
+// #include <QtCore/QDebug>
+// #include <qpa/qplatformintegrationplugin.h>
+//
+// QT_BEGIN_NAMESPACE
+//
+// class QPepperIntegrationPlugin : public QPlatformIntegrationPlugin
+// {
+//     Q_OBJECT
+//     Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.1" FILE
+//                           "pepper.json")
+// public:
+//     QStringList keys() const;
+//     QPlatformIntegration *create(const QString &, const QStringList &) Q_DECL_OVERRIDE;
+// };
+//
+// QStringList QPepperIntegrationPlugin::keys() const
+// {
+//     QStringList list;
+//     list << QStringLiteral("pepper");
+//     return list;
+// }
+//
+// QPlatformIntegration *QPepperIntegrationPlugin::create(const QString &system,
+//                                                        const QStringList &paramList)
+// {
+//     Q_UNUSED(paramList);
+//     // qDebug() << "QPepperIntegrationPlugin::create" << system;
+//     if (system.toLower() == QStringLiteral("pepper"))
+//         return new QPepperIntegration;
+//     return 0;
+// }
+//
+// QT_END_NAMESPACE
+//
+// #include "qpepperpluginmain.moc"
diff --git a/src/tools/nacldeployqt/main.cpp b/src/tools/nacldeployqt/main.cpp
index cdc982e8f814bee442efbcfc7ebcf13725a3dfac..418c5fc4e74ae10cff9c1771efe827d4474dffee 100644
--- a/src/tools/nacldeployqt/main.cpp
+++ b/src/tools/nacldeployqt/main.cpp
@@ -46,7 +46,7 @@ private:
     QString findPNaClTool(const QString &sdkRoot, const QString &toolName);
     QList<QByteArray> quote(const QList<QByteArray> &list);
     void runCommand(const QString &command);
-    bool copyRecursively(const QString &srcFilePath, const QString &tgtFilePath);
+    bool copyRecursively(const QString &srcFilePath, const QString &tgtFilePath, bool skipBinaries);
     QByteArray instantiateTemplate(const QByteArray &tmplate);
     void instantiateWriteTemplate(const QByteArray &tmplate, const QString &filePath);
 };
@@ -172,7 +172,11 @@ int QtNaclDeployer::deploy()
             QString target = targetBase + "/" + import;
 
             // TODO: Skip the binaries for static builds; they will be built into the main nexe
-            copyRecursively(source, target);
+            bool skipBinaries = false;
+            if (deploymentType == Emscripten) {
+                skipBinaries = true;
+            }
+            copyRecursively(source, target, skipBinaries);
         }
     }
 
@@ -390,7 +394,8 @@ void QtNaclDeployer::runCommand(const QString &command)
 }
 
 bool QtNaclDeployer::copyRecursively(const QString &srcFilePath,
-                                            const QString &tgtFilePath)
+                                            const QString &tgtFilePath,
+                                     bool skipBinaries)
 {
     QFileInfo srcFileInfo(srcFilePath);
     if (srcFileInfo.isDir()) {
@@ -402,11 +407,14 @@ bool QtNaclDeployer::copyRecursively(const QString &srcFilePath,
         QDir sourceDir(srcFilePath);
         QStringList fileNames = sourceDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
         foreach (const QString &fileName, fileNames) {
+            if (skipBinaries && (fileName.endsWith(".so") || fileName.endsWith(".a"))) {
+                continue;
+            }
             const QString newSrcFilePath
                     = srcFilePath + QLatin1Char('/') + fileName;
             const QString newTgtFilePath
                     = tgtFilePath + QLatin1Char('/') + fileName;
-            if (!copyRecursively(newSrcFilePath, newTgtFilePath))
+            if (!copyRecursively(newSrcFilePath, newTgtFilePath, skipBinaries))
                 return false;
         }
     } else {
diff --git a/src/tools/nacldeployqt/template_qtloader.cpp b/src/tools/nacldeployqt/template_qtloader.cpp
index fdf3d313adf05592901352138e3ea9725820840e..201e387066ebe25d30df4c53969686c762d48f0e 100644
--- a/src/tools/nacldeployqt/template_qtloader.cpp
+++ b/src/tools/nacldeployqt/template_qtloader.cpp
@@ -2,7 +2,7 @@ const char *templateQtLoader = R"STRING_DELIMITER(
 // This script is generated by nacldeployqt as a part of the standard
 // Qt deployment. It can be used either as-is or as a basis for a
 // custom deployment solution. Note that some parts of this script
-// (such as onMessage) is required by Qt.
+// (such as onMessage) are required by Qt.
 //
 // Usage:
 //
@@ -49,6 +49,10 @@ const char *templateQtLoader = R"STRING_DELIMITER(
 //     1) As argn, argv key/value pairs to Init() of a QPepperInstance subclass.
 //     2) As environment variables: toUpper(key)=value.
 
+var doCreateInstance = function () {
+    console.log("Please call QtLoader.load() instead of loading your application script.")
+}
+
 function QtLoader(config) {
     var self = this;
     self.config = config;
@@ -182,14 +186,6 @@ function onLoad(event)
     self.embed.style.visibility = "visible"
 }
 
-function loadScript(src, onload)
-{
-    var script = document.createElement('script')
-    script.src = src
-    script.onload = function () { onload() };
-    document.head.appendChild(script);
-}
-
 // Create Qt container element, possibly re-using existingElement
 function createElement(existingElement)
 {
@@ -263,15 +259,20 @@ function loadEmscripten()
     var height = self.listener.offsetHeight
 
     var embed = document.createElement("div");
+    self.embed = embed
     embed.setAttribute("class", "qt-embed");
     self.listener.appendChild(embed);
     self.listener.embed = embed;
 
-    loadScript(config.src, function(){
+    doCreateInstance = function() {
         CreateInstance(width, height, embed);
         embed.finishLoading();
-    })
-}
+    };
+
+    var script = document.createElement('script')
+    script.src = config.src;
+    document.head.appendChild(script);
+};
 
 function postMessage(message) {
     self.embed.postMessage(message)