author | Michael Wu <mwu@mozilla.com> |
Wed, 28 Nov 2012 13:43:19 -0500 | |
changeset 114648 | ab440f162ab8b884c749faee7c099616af0e7678 |
parent 114647 | 40e0504245634733607cb164e16b30ff91ee2d63 |
child 114649 | a4c6aedf00deb3d3683a5253c88e42acb3b22e29 |
push id | 23926 |
push user | ryanvm@gmail.com |
push date | Sat, 01 Dec 2012 15:27:30 +0000 |
treeherder | mozilla-central@ecdf0e332f17 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | cjones, joe |
bugs | 809665 |
milestone | 20.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
new file mode 100644 --- /dev/null +++ b/b2g/app/BootAnimation.cpp @@ -0,0 +1,657 @@ +/* Copyright 2012 Mozilla Foundation and Mozilla contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <GLES2/gl2.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include <algorithm> +#include <endian.h> +#include <fcntl.h> +#include <string> +#include <sys/mman.h> +#include <sys/stat.h> +#include <vector> +#include "mozilla/Util.h" +#include "mozilla/NullPtr.h" +#include "png.h" + +#include "android/log.h" +#include "ui/FramebufferNativeWindow.h" +#include "hardware_legacy/power.h" + +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args) +#define LOGW(args...) __android_log_print(ANDROID_LOG_WARN, "Gonk", ## args) +#define LOGE(args...) __android_log_print(ANDROID_LOG_ERROR, "Gonk", ## args) + +using namespace android; +using namespace mozilla; +using namespace std; + +static sp<FramebufferNativeWindow> gNativeWindow; +static pthread_t sAnimationThread; +static bool sRunAnimation; + +/* See http://www.pkware.com/documents/casestudies/APPNOTE.TXT */ +struct local_file_header { + uint32_t signature; + uint16_t min_version; + uint16_t general_flag; + uint16_t compression; + uint16_t lastmod_time; + uint16_t lastmod_date; + uint32_t crc32; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t filename_size; + uint16_t extra_field_size; + char data[0]; + + uint32_t GetDataSize() const + { + return letoh32(uncompressed_size); + } + + uint32_t GetSize() const + { + /* XXX account for data descriptor */ + return sizeof(local_file_header) + letoh16(filename_size) + + letoh16(extra_field_size) + GetDataSize(); + } + + const char * GetData() const + { + return data + letoh16(filename_size) + letoh16(extra_field_size); + } +} __attribute__((__packed__)); + +struct data_descriptor { + uint32_t crc32; + uint32_t compressed_size; + uint32_t uncompressed_size; +} __attribute__((__packed__)); + +struct cdir_entry { + uint32_t signature; + uint16_t creator_version; + uint16_t min_version; + uint16_t general_flag; + uint16_t compression; + uint16_t lastmod_time; + uint16_t lastmod_date; + uint32_t crc32; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t filename_size; + uint16_t extra_field_size; + uint16_t file_comment_size; + uint16_t disk_num; + uint16_t internal_attr; + uint32_t external_attr; + uint32_t offset; + char data[0]; + + uint32_t GetDataSize() const + { + return letoh32(compressed_size); + } + + uint32_t GetSize() const + { + return sizeof(cdir_entry) + letoh16(filename_size) + + letoh16(extra_field_size) + letoh16(file_comment_size); + } + + bool Valid() const + { + return signature == htole32(0x02014b50); + } +} __attribute__((__packed__)); + +struct cdir_end { + uint32_t signature; + uint16_t disk_num; + uint16_t cdir_disk; + uint16_t disk_entries; + uint16_t cdir_entries; + uint32_t cdir_size; + uint32_t cdir_offset; + uint16_t comment_size; + char comment[0]; + + bool Valid() const + { + return signature == htole32(0x06054b50); + } +} __attribute__((__packed__)); + +/* We don't have access to libjar and the zip reader in android + * doesn't quite fit what we want to do. */ +class ZipReader { + const char *mBuf; + const cdir_end *mEnd; + const char *mCdir_limit; + uint32_t mBuflen; + +public: + ZipReader() : mBuf(nullptr) {} + ~ZipReader() { + if (mBuf) + munmap((void *)mBuf, mBuflen); + } + + bool OpenArchive(const char *path) + { + int fd; + do { + fd = open(path, O_RDONLY); + } while (fd == -1 && errno == EINTR); + if (fd == -1) + return false; + + struct stat sb; + if (fstat(fd, &sb) == -1 || sb.st_size < sizeof(cdir_end)) { + close(fd); + return false; + } + + mBuflen = sb.st_size; + mBuf = (char *)mmap(nullptr, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); + close(fd); + + if (!mBuf) { + return false; + } + + madvise(mBuf, sb.st_size, MADV_SEQUENTIAL); + + mEnd = (cdir_end *)(mBuf + mBuflen - sizeof(cdir_end)); + while (!mEnd->Valid() && + (char *)mEnd > mBuf) { + mEnd = (cdir_end *)((char *)mEnd - 1); + } + + mCdir_limit = mBuf + letoh32(mEnd->cdir_offset) + letoh32(mEnd->cdir_size); + + if (!mEnd->Valid() || mCdir_limit > (char *)mEnd) { + munmap((void *)mBuf, mBuflen); + mBuf = nullptr; + return false; + } + + return true; + } + + /* Pass null to get the first cdir entry */ + const cdir_entry * GetNextEntry(const cdir_entry *prev) + { + const cdir_entry *entry; + if (prev) + entry = (cdir_entry *)((char *)prev + prev->GetSize()); + else + entry = (cdir_entry *)(mBuf + letoh32(mEnd->cdir_offset)); + + if (((char *)entry + entry->GetSize()) > mCdir_limit || + !entry->Valid()) + return nullptr; + return entry; + } + + string GetEntryName(const cdir_entry *entry) + { + uint16_t len = letoh16(entry->filename_size); + + string name; + name.append(entry->data, len); + return name; + } + + const local_file_header * GetLocalEntry(const cdir_entry *entry) + { + const local_file_header * data = + (local_file_header *)(mBuf + letoh32(entry->offset)); + if (((char *)data + data->GetSize()) > (char *)mEnd) + return nullptr; + return data; + } +}; + +struct AnimationFrame { + char path[256]; + char *buf; + uint16_t width; + uint16_t height; + const local_file_header *file; + + AnimationFrame() : buf(nullptr) {} + AnimationFrame(const AnimationFrame &frame) : buf(nullptr) { + strncpy(path, frame.path, sizeof(path)); + file = frame.file; + } + ~AnimationFrame() + { + if (buf) + free(buf); + } + + bool operator<(const AnimationFrame &other) const + { + return strcmp(path, other.path) < 0; + } + + void ReadPngFrame(); +}; + +struct AnimationPart { + int32_t count; + int32_t pause; + char path[256]; + vector<AnimationFrame> frames; +}; + +using namespace android; + +struct RawReadState { + const char *start; + uint32_t offset; + uint32_t length; +}; + +static void +RawReader(png_structp png_ptr, png_bytep data, png_size_t length) +{ + RawReadState *state = (RawReadState *)png_get_io_ptr(png_ptr); + if (length > (state->length - state->offset)) + png_err(png_ptr); + + memcpy(data, state->start + state->offset, length); + state->offset += length; +} + +void +AnimationFrame::ReadPngFrame() +{ + png_structp pngread = png_create_read_struct(PNG_LIBPNG_VER_STRING, + nullptr, nullptr, nullptr); + + png_infop pnginfo = png_create_info_struct(pngread); + + RawReadState state; + state.start = file->GetData(); + state.length = file->GetDataSize(); + state.offset = 0; + + png_set_read_fn(pngread, &state, RawReader); + + setjmp(png_jmpbuf(pngread)); + + png_read_info(pngread, pnginfo); + + width = png_get_image_width(pngread, pnginfo); + height = png_get_image_height(pngread, pnginfo); + buf = (char *)malloc(width * height * 3); + + vector<char *> rows(height + 1); + uint32_t stride = width * 3; + for (int i = 0; i < height; i++) { + rows[i] = buf + (stride * i); + } + rows[height] = nullptr; + png_set_palette_to_rgb(pngread); + png_read_image(pngread, (png_bytepp)&rows.front()); + png_destroy_read_struct(&pngread, &pnginfo, nullptr); +} + +static const EGLint kEGLConfigAttribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE +}; + +static bool +CreateConfig(EGLConfig* aConfig, EGLDisplay display, int format) +{ + EGLConfig configs[64]; + EGLint ncfg = ArrayLength(configs); + + if (!eglChooseConfig(display, kEGLConfigAttribs, + configs, ncfg, &ncfg) || + ncfg < 1) { + return false; + } + + for (int j = 0; j < ncfg; ++j) { + EGLConfig config = configs[j]; + EGLint id; + + if (eglGetConfigAttrib(display, config, + EGL_NATIVE_VISUAL_ID, &id) && + id > 0 && id == format) + { + *aConfig = config; + return true; + } + } + return false; +} + +static void * +AnimationThread(void *) +{ + ZipReader reader; + if (!reader.OpenArchive("/system/media/bootanimation.zip")) { + LOGW("Could not open boot animation"); + return nullptr; + } + + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglInitialize(display, nullptr, nullptr); + + int format; + ANativeWindow const * const window = gNativeWindow.get(); + window->query(window, NATIVE_WINDOW_FORMAT, &format); + + EGLConfig config = NULL; + CreateConfig(&config, display, format); + if (!config) { + LOGW("Could not find config for pixel format"); + return nullptr; + } + + EGLSurface surface = eglCreateWindowSurface(display, config, gNativeWindow.get(), nullptr); + + const cdir_entry *entry = nullptr; + const local_file_header *file = nullptr; + while ((entry = reader.GetNextEntry(entry))) { + string name = reader.GetEntryName(entry); + if (!name.compare("desc.txt")) { + file = reader.GetLocalEntry(entry); + break; + } + } + + if (!file) { + LOGW("Could not find desc.txt in boot animation"); + return nullptr; + } + + string descCopy; + descCopy.append(file->GetData(), entry->GetDataSize()); + int32_t width, height, fps; + const char *line = descCopy.c_str(); + const char *end; + bool headerRead = true; + vector<AnimationPart> parts; + + /* + * bootanimation.zip + * + * This is the boot animation file format that Android uses. + * It's a zip file with a directories containing png frames + * and a desc.txt that describes how they should be played. + * + * desc.txt contains two types of lines + * 1. [width] [height] [fps] + * There is one of these lines per bootanimation. + * If the width and height are smaller than the screen, + * the frames are centered on a black background. + * XXX: Currently we stretch instead of centering the frame. + * 2. p [count] [pause] [path] + * This describes one animation part. + * Each animation part is played in sequence. + * An animation part contains all the files/frames in the + * directory specified in [path] + * [count] indicates the number of times this part repeats. + * [pause] indicates the number of frames that this part + * should pause for after playing the full sequence but + * before repeating. + */ + + do { + end = strstr(line, "\n"); + + AnimationPart part; + if (headerRead && + sscanf(line, "%d %d %d", &width, &height, &fps) == 3) { + headerRead = false; + } else if (sscanf(line, "p %d %d %s", + &part.count, &part.pause, part.path)) { + parts.push_back(part); + } + } while (end && *(line = end + 1)); + + for (uint32_t i = 0; i < parts.size(); i++) { + AnimationPart &part = parts[i]; + entry = nullptr; + char search[256]; + snprintf(search, sizeof(search), "%s/", part.path); + while ((entry = reader.GetNextEntry(entry))) { + string name = reader.GetEntryName(entry); + if (name.find(search) || + !entry->GetDataSize() || + name.length() >= 256) + continue; + + part.frames.push_back(); + AnimationFrame &frame = part.frames.back(); + strcpy(frame.path, name.c_str()); + frame.file = reader.GetLocalEntry(entry); + } + + sort(part.frames.begin(), part.frames.end()); + } + + static EGLint gContextAttribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE, 0 + }; + EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, gContextAttribs); + + eglMakeCurrent(display, surface, surface, context); + glEnable(GL_TEXTURE_2D); + + const char *vsString = + "attribute vec2 aPosition; " + "attribute vec2 aTexCoord; " + "varying vec2 vTexCoord; " + "void main() { " + " gl_Position = vec4(aPosition, 0.0, 1.0); " + " vTexCoord = aTexCoord; " + "}"; + + const char *fsString = + "precision mediump float; " + "varying vec2 vTexCoord; " + "uniform sampler2D sTexture; " + "void main() { " + " gl_FragColor = vec4(texture2D(sTexture, vTexCoord).rgb, 1.0); " + "}"; + + GLint status; + GLuint vsh = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vsh, 1, &vsString, nullptr); + glCompileShader(vsh); + glGetShaderiv(vsh, GL_COMPILE_STATUS, &status); + if (!status) { + LOGE("Failed to compile vertex shader"); + return nullptr; + } + + GLuint fsh = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fsh, 1, &fsString, nullptr); + glCompileShader(fsh); + glGetShaderiv(fsh, GL_COMPILE_STATUS, &status); + if (!status) { + LOGE("Failed to compile fragment shader"); + return nullptr; + } + + GLuint programId = glCreateProgram(); + glAttachShader(programId, vsh); + glAttachShader(programId, fsh); + + glLinkProgram(programId); + glGetProgramiv(programId, GL_LINK_STATUS, &status); + if (!status) { + LOG("Failed to link program"); + return nullptr; + } + + GLint positionLoc = glGetAttribLocation(programId, "aPosition"); + GLint texCoordLoc = glGetAttribLocation(programId, "aTexCoord"); + GLint textureLoc = glGetUniformLocation(programId, "sTexture"); + + glUseProgram(programId); + + GLfloat texCoords[] = { 0.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 1.0f, + 1.0f, 0.0f }; + + GLfloat vCoords[] = { -1.0f, -1.0f, + -1.0f, 1.0f, + 1.0f, -1.0f, + 1.0f, 1.0f }; + + GLuint rectBuf, texBuf; + glGenBuffers(1, &rectBuf); + glGenBuffers(1, &texBuf); + + GLuint tex; + glGenTextures(1, &tex); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glEnableVertexAttribArray(positionLoc); + glBindBuffer(GL_ARRAY_BUFFER, rectBuf); + glBufferData(GL_ARRAY_BUFFER, sizeof(vCoords), vCoords, GL_STATIC_DRAW); + glVertexAttribPointer(positionLoc, 2, GL_FLOAT, GL_FALSE, 0, 0); + + glEnableVertexAttribArray(texCoordLoc); + glBindBuffer(GL_ARRAY_BUFFER, texBuf); + glBufferData(GL_ARRAY_BUFFER, sizeof(texCoords), texCoords, GL_STATIC_DRAW); + glVertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glUniform1i(textureLoc, 0); + + uint32_t frameDelayUs = 1000000 / fps; + + for (uint32_t i = 0; i < parts.size(); i++) { + AnimationPart &part = parts[i]; + + uint32_t j = 0; + while (sRunAnimation && (!part.count || j++ < part.count)) { + for (uint32_t k = 0; k < part.frames.size(); k++) { + struct timeval tv1, tv2; + gettimeofday(&tv1, nullptr); + AnimationFrame &frame = part.frames[k]; + if (!frame.buf) { + frame.ReadPngFrame(); + } + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, + frame.width, frame.height, 0, + GL_RGB, GL_UNSIGNED_BYTE, frame.buf); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + gettimeofday(&tv2, nullptr); + + timersub(&tv2, &tv1, &tv2); + + if (tv2.tv_usec < frameDelayUs) { + usleep(frameDelayUs - tv2.tv_usec); + } else { + LOGW("Frame delay is %d us but decoding took %d us", frameDelayUs, tv2.tv_usec); + } + + eglSwapBuffers(display, surface); + + if (part.count && j >= part.count) { + free(frame.buf); + frame.buf = nullptr; + } + } + usleep(frameDelayUs * part.pause); + } + } + glBindTexture(GL_TEXTURE_2D, 0); + glUseProgram(0); + glDeleteTextures(1, &tex); + glDeleteBuffers(1, &texBuf); + glDeleteBuffers(1, &rectBuf); + glDeleteProgram(programId); + glDeleteShader(fsh); + glDeleteShader(vsh); + + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + eglDestroyContext(display, context); + eglDestroySurface(display, surface); + return nullptr; +} + +static int +CancelBufferNoop(ANativeWindow* aWindow, android_native_buffer_t* aBuffer) +{ + return 0; +} + +__attribute__ ((visibility ("default"))) +FramebufferNativeWindow* +NativeWindow() +{ + if (gNativeWindow.get()) { + return gNativeWindow.get(); + } + + // Some gralloc HALs need this in order to open the + // framebuffer device after we restart with the screen off. + // + // NB: this *must* run BEFORE allocating the + // FramebufferNativeWindow. Do not separate these two C++ + // statements. + set_screen_state(1); + + // We (apparently) don't have a way to tell if allocating the + // fbs succeeded or failed. + gNativeWindow = new FramebufferNativeWindow(); + + // Bug 776742: FrambufferNativeWindow doesn't set the cancelBuffer + // function pointer, causing EGL to segfault when the window surface + // is destroyed (i.e. on process exit). This workaround stops us + // from hard crashing in that situation. + gNativeWindow->cancelBuffer = CancelBufferNoop; + + sRunAnimation = true; + pthread_create(&sAnimationThread, nullptr, AnimationThread, nullptr); + + return gNativeWindow.get(); +} + + +__attribute__ ((visibility ("default"))) +void +StopBootAnimation() +{ + if (sRunAnimation) { + sRunAnimation = false; + pthread_join(sAnimationThread, nullptr); + } +}
new file mode 100644 --- /dev/null +++ b/b2g/app/BootAnimation.h @@ -0,0 +1,17 @@ +#ifndef BOOTANIMATION_H +#define BOOTANIMATION_H + +namespace android { +class FramebufferNativeWindow; +} + +/* This returns a FramebufferNativeWindow if one exists. + * If not, one is created and the boot animation is started. */ +__attribute__ ((weak)) +android::FramebufferNativeWindow* NativeWindow(); + +/* This stops the boot animation if it's still running. */ +__attribute__ ((weak)) +void StopBootAnimation(); + +#endif /* BOOTANIMATION_H */
--- a/b2g/app/Makefile.in +++ b/b2g/app/Makefile.in @@ -19,16 +19,31 @@ ifndef LIBXUL_SDK ifneq ($(GAIADIR),) PROGRAM=$(MOZ_APP_NAME)-bin$(BIN_SUFFIX) else PROGRAM=$(MOZ_APP_NAME)$(BIN_SUFFIX) endif CPPSRCS = nsBrowserApp.cpp +ifeq (gonk,$(MOZ_WIDGET_TOOLKIT)) +CPPSRCS += BootAnimation.cpp +LIBS += \ + -lGLESv2 \ + -lEGL \ + -lui \ + -lhardware_legacy \ + -lhardware \ + -lcutils \ + $(DEPTH)/media/libpng/$(LIB_PREFIX)mozpng.$(LIB_SUFFIX) \ + $(MOZ_ZLIB_LIBS) \ + $(NULL) +OS_LDFLAGS += -Wl,--export-dynamic +endif + LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/base LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/build LOCAL_INCLUDES += -I$(DEPTH)/build DEFINES += -DXPCOM_GLUE STL_FLAGS=
--- a/b2g/app/nsBrowserApp.cpp +++ b/b2g/app/nsBrowserApp.cpp @@ -23,16 +23,21 @@ #include "nsStringGlue.h" #ifdef XP_WIN // we want a wmain entry point #include "nsWindowsWMain.cpp" #define snprintf _snprintf #define strcasecmp _stricmp #endif + +#ifdef MOZ_WIDGET_GONK +#include "BootAnimation.h" +#endif + #include "BinaryPath.h" #include "nsXPCOMPrivate.h" // for MAXPATHLEN and XPCOM_DLL #include "mozilla/Telemetry.h" static void Output(const char *fmt, ... ) { @@ -134,16 +139,21 @@ static int do_main(int argc, char* argv[ Output("Couldn't set %s.\n", appEnv); return 255; } argv[2] = argv[0]; argv += 2; argc -= 2; } +#ifdef MOZ_WIDGET_GONK + /* Called to start the boot animation */ + (void) NativeWindow(); +#endif + if (appini) { nsXREAppData *appData; rv = XRE_CreateAppData(appini, &appData); if (NS_FAILED(rv)) { Output("Couldn't read application.ini"); return 255; } int result = XRE_main(argc, argv, appData, 0);
--- a/b2g/chrome/content/shell.js +++ b/b2g/chrome/content/shell.js @@ -398,16 +398,18 @@ var shell = { DOMApplicationRegistry.allAppsLaunchable = true; this.sendEvent(window, 'ContentStart'); content.addEventListener('load', function shell_homeLoaded() { content.removeEventListener('load', shell_homeLoaded); shell.isHomeLoaded = true; + Services.obs.notifyObservers(null, "browser-ui-startup-complete", ""); + if ('pendingChromeEvents' in shell) { shell.pendingChromeEvents.forEach((shell.sendChromeEvent).bind(shell)); } delete shell.pendingChromeEvents; }); break; case 'MozApplicationManifest':
--- a/media/libpng/mozpngconf.h +++ b/media/libpng/mozpngconf.h @@ -38,16 +38,23 @@ #define PNG_READ_GAMMA_SUPPORTED #define PNG_READ_GRAY_TO_RGB_SUPPORTED #define PNG_READ_INTERLACING_SUPPORTED #define PNG_READ_SCALE_16_TO_8_SUPPORTED #define PNG_READ_TEXT_SUPPORTED #define PNG_READ_TRANSFORMS_SUPPORTED #endif +/* necessary for boot animation code */ +#ifdef MOZ_WIDGET_GONK +#define PNG_SEQUENTIAL_READ_SUPPORTED +#define PNG_EASY_ACCESS_SUPPORTED +#define PNG_READ_EXPAND_SUPPORTED +#endif + #ifdef MOZ_PNG_WRITE #define PNG_WRITE_SUPPORTED #define PNG_WRITE_APNG_SUPPORTED #define PNG_WRITE_tRNS_SUPPORTED #define PNG_WRITE_16BIT_SUPPORTED #define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED #define PNG_WRITE_FLUSH_SUPPORTED #define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
--- a/widget/gonk/Makefile.in +++ b/widget/gonk/Makefile.in @@ -69,16 +69,17 @@ CPPSRCS = \ SHARED_LIBRARY_LIBS = ../xpwidgets/libxpwidgets_s.a include $(topsrcdir)/config/rules.mk DEFINES += -D_IMPL_NS_WIDGET -DHAVE_OFF64_T -DSK_BUILD_FOR_ANDROID_NDK LOCAL_INCLUDES += \ + -I$(topsrcdir)/b2g/app \ -I$(topsrcdir)/widget/xpwidgets \ -I$(topsrcdir)/widget/shared \ -I$(topsrcdir)/dom/system/android \ -I$(topsrcdir)/content/events/src \ -I$(topsrcdir)/gfx/skia/include/core \ -I$(topsrcdir)/gfx/skia/include/config \ -I$(srcdir) \ $(NULL)
--- a/widget/gonk/nsAppShell.cpp +++ b/widget/gonk/nsAppShell.cpp @@ -60,22 +60,25 @@ #else # define VERBOSE_LOG(args...) \ (void)0 #endif using namespace android; using namespace mozilla; using namespace mozilla::dom; +using namespace mozilla::services; bool gDrawRequest = false; static nsAppShell *gAppShell = NULL; static int epollfd = 0; static int signalfds[2] = {0}; +NS_IMPL_ISUPPORTS_INHERITED1(nsAppShell, nsBaseAppShell, nsIObserver) + namespace mozilla { bool ProcessNextEvent() { return gAppShell->ProcessNextNativeEvent(true); } void NotifyEvent() @@ -581,16 +584,17 @@ status_t GeckoInputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) { return OK; } nsAppShell::nsAppShell() : mNativeCallbackRequest(false) , mHandlers() + , mEnableDraw(false) { gAppShell = this; } nsAppShell::~nsAppShell() { // mReaderThread and mEventHub will both be null if InitInputDevices // is not called. @@ -619,26 +623,49 @@ nsAppShell::Init() int ret = pipe2(signalfds, O_NONBLOCK); NS_ENSURE_FALSE(ret, NS_ERROR_UNEXPECTED); rv = AddFdHandler(signalfds[0], pipeHandler, ""); NS_ENSURE_SUCCESS(rv, rv); InitGonkMemoryPressureMonitoring(); + nsCOMPtr<nsIObserverService> obsServ = GetObserverService(); + if (obsServ) { + obsServ->AddObserver(this, "browser-ui-startup-complete", false); + } + // Delay initializing input devices until the screen has been // initialized (and we know the resolution). return rv; } NS_IMETHODIMP +nsAppShell::Observe(nsISupports* aSubject, + const char* aTopic, + const PRUnichar* aData) +{ + if (strcmp(aTopic, "browser-ui-startup-complete")) { + return nsBaseAppShell::Observe(aSubject, aTopic, aData); + } + + mEnableDraw = true; + NotifyEvent(); + return NS_OK; +} + +NS_IMETHODIMP nsAppShell::Exit() { - OrientationObserver::ShutDown(); - return nsBaseAppShell::Exit(); + OrientationObserver::ShutDown(); + nsCOMPtr<nsIObserverService> obsServ = GetObserverService(); + if (obsServ) { + obsServ->RemoveObserver(this, "browser-ui-startup-complete"); + } + return nsBaseAppShell::Exit(); } void nsAppShell::InitInputDevices() { mEventHub = new EventHub(); mReaderPolicy = new GeckoInputReaderPolicy(); mReaderPolicy->setDisplayInfo(); @@ -700,17 +727,17 @@ nsAppShell::ProcessNextNativeEvent(bool // NativeEventCallback always schedules more if it needs it // so we can coalesce these. // See the implementation in nsBaseAppShell.cpp for more info if (mNativeCallbackRequest) { mNativeCallbackRequest = false; NativeEventCallback(); } - if (gDrawRequest) { + if (gDrawRequest && mEnableDraw) { gDrawRequest = false; nsWindow::DoDraw(); } return true; } void
--- a/widget/gonk/nsAppShell.h +++ b/widget/gonk/nsAppShell.h @@ -59,16 +59,19 @@ class InputReaderThread; class GeckoInputReaderPolicy; class GeckoInputDispatcher; class nsAppShell : public nsBaseAppShell { public: nsAppShell(); + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIOBSERVER + nsresult Init(); NS_IMETHOD Exit() MOZ_OVERRIDE; virtual bool ProcessNextNativeEvent(bool maywait); void NotifyNativeEvent(); @@ -82,16 +85,21 @@ protected: private: nsresult AddFdHandler(int fd, FdHandlerCallback handlerFunc, const char* deviceName); void InitInputDevices(); // This is somewhat racy but is perfectly safe given how the callback works bool mNativeCallbackRequest; + + // This gets flipped when we observe a browser-ui-startup-complete. + // browser-ui-startup-complete means that we're really ready to draw + // and can stop the boot animation + bool mEnableDraw; nsTArray<FdHandler> mHandlers; android::sp<android::EventHub> mEventHub; android::sp<GeckoInputReaderPolicy> mReaderPolicy; android::sp<GeckoInputDispatcher> mDispatcher; android::sp<android::InputReader> mReader; android::sp<android::InputReaderThread> mReaderThread; };
--- a/widget/gonk/nsWindow.cpp +++ b/widget/gonk/nsWindow.cpp @@ -10,25 +10,27 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <EGL/egl.h> #include <EGL/eglext.h> + #include <fcntl.h> #include "android/log.h" #include "ui/FramebufferNativeWindow.h" #include "mozilla/dom/TabParent.h" #include "mozilla/Hal.h" #include "mozilla/Preferences.h" #include "mozilla/FileUtils.h" +#include "BootAnimation.h" #include "Framebuffer.h" #include "gfxContext.h" #include "gfxPlatform.h" #include "gfxUtils.h" #include "GLContextProvider.h" #include "HwcComposer2D.h" #include "LayerManagerOGL.h" #include "nsAutoPtr.h" @@ -59,57 +61,25 @@ static uint32_t sScreenRotation; static uint32_t sPhysicalScreenRotation; static nsIntRect sVirtualBounds; static gfxMatrix sRotationMatrix; static nsRefPtr<GLContext> sGLContext; static nsTArray<nsWindow *> sTopWindows; static nsWindow *gWindowToRedraw = nullptr; static nsWindow *gFocusedWindow = nullptr; -static android::FramebufferNativeWindow *gNativeWindow = nullptr; static bool sFramebufferOpen; static bool sUsingOMTC; static bool sUsingHwc; static bool sScreenInitialized; static nsRefPtr<gfxASurface> sOMTCSurface; static pthread_t sFramebufferWatchThread; namespace { -static int -CancelBufferNoop(ANativeWindow* aWindow, android_native_buffer_t* aBuffer) -{ - return 0; -} - -android::FramebufferNativeWindow* -NativeWindow() -{ - if (!gNativeWindow) { - // Some gralloc HALs need this in order to open the - // framebuffer device after we restart with the screen off. - // - // NB: this *must* run BEFORE allocating the - // FramebufferNativeWindow. Do not separate these two C++ - // statements. - hal::SetScreenEnabled(true); - - // We (apparently) don't have a way to tell if allocating the - // fbs succeeded or failed. - gNativeWindow = new android::FramebufferNativeWindow(); - - // Bug 776742: FrambufferNativeWindow doesn't set the cancelBuffer - // function pointer, causing EGL to segfault when the window surface - // is destroyed (i.e. on process exit). This workaround stops us - // from hard crashing in that situation. - gNativeWindow->cancelBuffer = CancelBufferNoop; - } - return gNativeWindow; -} - static uint32_t EffectiveScreenRotation() { return (sScreenRotation + sPhysicalScreenRotation) % (360 / 90); } class ScreenOnOffEvent : public nsRunnable { public: @@ -238,16 +208,18 @@ nsWindow::DoDraw(void) return; } if (!gWindowToRedraw) { LOG(" no window to draw, bailing"); return; } + StopBootAnimation(); + nsIntRegion region = gWindowToRedraw->mDirtyRegion; gWindowToRedraw->mDirtyRegion.SetEmpty(); LayerManager* lm = gWindowToRedraw->GetLayerManager(); if (mozilla::layers::LAYERS_OPENGL == lm->GetBackendType()) { LayerManagerOGL* oglm = static_cast<LayerManagerOGL*>(lm); oglm->SetClippingRegion(region); oglm->SetWorldTransform(sRotationMatrix);