From: Eurogiciel-BOT Date: Fri, 4 Apr 2014 08:14:11 +0000 (+0000) Subject: Upstream version 6.34.113.0 X-Git-Tag: accepted/tizen/generic/20140404.092229^0 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fframework%2Fweb%2Fcrosswalk.git;a=commitdiff_plain;h=1e23ad6541c47511c5a4f268201d7ae4371bb979 Upstream version 6.34.113.0 Upstream commit-id 7088b1669e5a2fb54fbd52cc4420e5769d859632 Change-Id: I414bcd47d2d419bb46a449d23fe9dde0b0389c2d Signed-off-by: Eurogiciel-BOT --- diff --git a/packaging/crosswalk-2.31-enable-VAVDA-with-EGL.patch b/packaging/crosswalk-2.31-enable-VAVDA-with-EGL.patch deleted file mode 100644 index 6595561..0000000 --- a/packaging/crosswalk-2.31-enable-VAVDA-with-EGL.patch +++ /dev/null @@ -1,1313 +0,0 @@ -From c9f2fa16578bc20c83247e72608b5d6ca4dff6ba Mon Sep 17 00:00:00 2001 -From: "qing.zhang" -Date: Thu, 7 Nov 2013 08:59:38 -0500 -Subject: [PATCH] [Tizen] Enabling Hardware Acceleration with Libva and EGL in - VDA for Tizen Mobile within chromium v31+. - -Why we need to maintain it in our side: -=========================================== -1) Upstream confirm VAVDA will continue to be restricted to - CrOS/X86 for dev & testing only and not for chromium road map. -2) CrOS/X86 no plan to expend EGL backend which finalize in - June 2012 and be addressed to the CrOS graphics team. - -So, the upstream no plan to lerage VAVDA with EGL graphic - backend for any X86. - -3) The tizen-mobile's driver only support EGL as texture - backend. The video hw acceleration of xwalk have to - rely on EGL not GLX to bind decoded pixmap. -=========================================== -That's why we enable specific EGL for VAVDA in tizen port. ---- - .../gpu/media/gpu_video_decode_accelerator.cc | 8 + - .../media/vaapi_video_decode_accelerator_tizen.cc | 908 +++++++++++++++++++++ - .../media/vaapi_video_decode_accelerator_tizen.h | 273 +++++++ - content/content_common.gypi | 26 + - 4 files changed, 1215 insertions(+) - create mode 100644 content/common/gpu/media/vaapi_video_decode_accelerator_tizen.cc - create mode 100644 content/common/gpu/media/vaapi_video_decode_accelerator_tizen.h - -diff --git a/content/common/gpu/media/gpu_video_decode_accelerator.cc b/content/common/gpu/media/gpu_video_decode_accelerator.cc -index bd1dc5f..c5a6df2 100644 ---- a/content/common/gpu/media/gpu_video_decode_accelerator.cc -+++ b/content/common/gpu/media/gpu_video_decode_accelerator.cc -@@ -32,6 +32,8 @@ - #include "content/common/gpu/media/vaapi_video_decode_accelerator.h" - #include "ui/gl/gl_context_glx.h" - #include "ui/gl/gl_implementation.h" -+#elif defined(OS_TIZEN_MOBILE) && defined(ARCH_CPU_X86_FAMILY) -+#include "content/common/gpu/media/vaapi_video_decode_accelerator_tizen.h" - #elif defined(OS_ANDROID) - #include "content/common/gpu/media/android_video_decode_accelerator.h" - #endif -@@ -296,6 +298,12 @@ void GpuVideoDecodeAccelerator::Initialize( - static_cast(stub_->decoder()->GetGLContext()); - video_decode_accelerator_.reset(new VaapiVideoDecodeAccelerator( - glx_context->display(), this, make_context_current_)); -+#elif defined(OS_TIZEN_MOBILE) && defined(ARCH_CPU_X86_FAMILY) -+ video_decode_accelerator_.reset(new VaapiVideoDecodeAccelerator( -+ gfx::GLSurfaceEGL::GetHardwareDisplay(), -+ stub_->decoder()->GetGLContext()->GetHandle(), -+ this, -+ make_context_current_)); - #elif defined(OS_ANDROID) - video_decode_accelerator_.reset(new AndroidVideoDecodeAccelerator( - this, -diff --git a/content/common/gpu/media/vaapi_video_decode_accelerator_tizen.cc b/content/common/gpu/media/vaapi_video_decode_accelerator_tizen.cc -new file mode 100644 -index 0000000..cfff457 ---- /dev/null -+++ b/content/common/gpu/media/vaapi_video_decode_accelerator_tizen.cc -@@ -0,0 +1,908 @@ -+// Copyright (c) 2013 Intel Corporation. All rights reserved. -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+ -+#include "base/bind.h" -+#include "base/debug/trace_event.h" -+#include "base/logging.h" -+#include "base/metrics/histogram.h" -+#include "base/stl_util.h" -+#include "base/strings/string_util.h" -+#include "base/synchronization/waitable_event.h" -+#include "content/child/child_thread.h" -+#include "content/common/gpu/gpu_channel.h" -+#include "content/common/gpu/media/vaapi_video_decode_accelerator_tizen.h" -+#include "media/base/bind_to_current_loop.h" -+#include "media/video/picture.h" -+#include "ui/gl/gl_bindings.h" -+#include "ui/gl/scoped_binders.h" -+ -+static void ReportToUMA( -+ content::VaapiH264Decoder::VAVDAH264DecoderFailure failure) { -+ UMA_HISTOGRAM_ENUMERATION( -+ "Media.VAVDAH264.DecoderFailure", -+ failure, -+ content::VaapiH264Decoder::VAVDA_H264_DECODER_FAILURES_MAX); -+} -+ -+namespace content { -+ -+#define RETURN_AND_NOTIFY_ON_FAILURE(result, log, error_code, ret) \ -+ do { \ -+ if (!(result)) { \ -+ DVLOG(1) << log; \ -+ NotifyError(error_code); \ -+ return ret; \ -+ } \ -+ } while (0) -+ -+VaapiVideoDecodeAccelerator::InputBuffer::InputBuffer() : id(0), size(0) { -+} -+ -+VaapiVideoDecodeAccelerator::InputBuffer::~InputBuffer() { -+} -+ -+void VaapiVideoDecodeAccelerator::NotifyError(Error error) { -+ if (message_loop_ != base::MessageLoop::current()) { -+ DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current()); -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::NotifyError, weak_this_, error)); -+ return; -+ } -+ -+ // Post Cleanup() as a task so we don't recursively acquire lock_. -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::Cleanup, weak_this_)); -+ -+ DVLOG(1) << "Notifying of error " << error; -+ if (client_) { -+ client_->NotifyError(error); -+ client_ptr_factory_.InvalidateWeakPtrs(); -+ } -+} -+ -+// TFPPicture allocates X Pixmaps and binds them to textures passed -+// in PictureBuffers from clients to them. TFPPictures are created as -+// a consequence of receiving a set of PictureBuffers from clients and released -+// at the end of decode (or when a new set of PictureBuffers is required). -+// -+// TFPPictures are used for output, contents of VASurfaces passed from decoder -+// are put into the associated pixmap memory and sent to client. -+class VaapiVideoDecodeAccelerator::TFPPicture { -+ public: -+ ~TFPPicture(); -+ -+ static linked_ptr Create( -+ const base::Callback& make_context_current, -+ EGLDisplay egl_display, -+ Display* x_display, -+ int32 picture_buffer_id, -+ uint32 texture_id, -+ gfx::Size size); -+ int32 picture_buffer_id() { -+ return picture_buffer_id_; -+ } -+ -+ uint32 texture_id() { -+ return texture_id_; -+ } -+ -+ gfx::Size size() { -+ return size_; -+ } -+ -+ int x_pixmap() { -+ return x_pixmap_; -+ } -+ -+ // Bind texture to pixmap. Needs to be called every frame. -+ bool Bind(); -+ -+ private: -+ TFPPicture(const base::Callback& make_context_current, -+ Display* x_display, -+ int32 picture_buffer_id, -+ uint32 texture_id, -+ gfx::Size size); -+ -+ bool Initialize(EGLDisplay egl_display); -+ -+ base::Callback make_context_current_; -+ -+ Display* x_display_; -+ -+ // Output id for the client. -+ int32 picture_buffer_id_; -+ uint32 texture_id_; -+ -+ gfx::Size size_; -+ -+ // Pixmaps bound to this texture. -+ Pixmap x_pixmap_; -+ EGLDisplay egl_display_; -+ EGLImageKHR egl_image_; -+ -+ DISALLOW_COPY_AND_ASSIGN(TFPPicture); -+}; -+ -+VaapiVideoDecodeAccelerator::TFPPicture::TFPPicture( -+ const base::Callback& make_context_current, -+ Display* x_display, -+ int32 picture_buffer_id, -+ uint32 texture_id, -+ gfx::Size size) -+ : make_context_current_(make_context_current), -+ x_display_(x_display), -+ picture_buffer_id_(picture_buffer_id), -+ texture_id_(texture_id), -+ size_(size), -+ x_pixmap_(0), -+ egl_image_(0) { -+ DCHECK(!make_context_current_.is_null()); -+}; -+ -+linked_ptr -+VaapiVideoDecodeAccelerator::TFPPicture::Create( -+ const base::Callback& make_context_current, -+ EGLDisplay egl_display, -+ Display* x_display, -+ int32 picture_buffer_id, -+ uint32 texture_id, -+ gfx::Size size) { -+ -+ linked_ptr tfp_picture( -+ new TFPPicture(make_context_current, x_display, -+ picture_buffer_id, texture_id, size)); -+ -+ if (!tfp_picture->Initialize(egl_display)) -+ tfp_picture.reset(); -+ -+ return tfp_picture; -+} -+ -+bool VaapiVideoDecodeAccelerator::TFPPicture::Initialize( -+ EGLDisplay egl_display) { -+ // Check for NULL prevents unittests from crashing on nonexistent ChildThread. -+ DCHECK(ChildThread::current() == NULL || -+ ChildThread::current()->message_loop() == base::MessageLoop::current()); -+ -+ if (!make_context_current_.Run()) -+ return false; -+ -+ XWindowAttributes win_attr; -+ int screen = DefaultScreen(x_display_); -+ XGetWindowAttributes(x_display_, RootWindow(x_display_, screen), &win_attr); -+ //TODO(posciak): pass the depth required by libva, not the RootWindow's depth -+ x_pixmap_ = XCreatePixmap(x_display_, RootWindow(x_display_, screen), -+ size_.width(), size_.height(), win_attr.depth); -+ if (!x_pixmap_) { -+ DVLOG(1) << "Failed creating an X Pixmap for TFP"; -+ return false; -+ } -+ -+ egl_display_ = egl_display; -+ EGLint image_attrs[] = { EGL_IMAGE_PRESERVED_KHR, 1 , EGL_NONE }; -+ -+ egl_image_ = eglCreateImageKHR(egl_display_, -+ EGL_NO_CONTEXT, -+ EGL_NATIVE_PIXMAP_KHR, -+ (EGLClientBuffer)x_pixmap_, -+ image_attrs); -+ if (!egl_image_) { -+ DVLOG(1) << "Failed creating a EGLImage from Pixmap for KHR"; -+ return false; -+ } -+ -+ return true; -+} -+VaapiVideoDecodeAccelerator::TFPPicture::~TFPPicture() { -+ // Check for NULL prevents unittests from crashing on non-existing ChildThread. -+ DCHECK(ChildThread::current() == NULL || -+ ChildThread::current()->message_loop() == base::MessageLoop::current()); -+ -+ // Unbind surface from texture and deallocate resources. -+ if (make_context_current_.Run()) { -+ eglDestroyImageKHR(egl_display_, egl_image_); -+ } -+ -+ if (x_pixmap_) -+ XFreePixmap(x_display_, x_pixmap_); -+ XSync(x_display_, False); // Needed to work around buggy vdpau-driver. -+} -+ -+bool VaapiVideoDecodeAccelerator::TFPPicture::Bind() { -+ DCHECK(x_pixmap_); -+ DCHECK(egl_image_); -+ -+ // Check for NULL prevents unittests from crashing on nonexistent ChildThread. -+ DCHECK(ChildThread::current() == NULL || -+ ChildThread::current()->message_loop() == base::MessageLoop::current()); -+ -+ if (!make_context_current_.Run()) -+ return false; -+ -+ gfx::ScopedTextureBinder texture_binder(GL_TEXTURE_2D, texture_id_); -+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_); -+ -+ return true; -+} -+ -+VaapiVideoDecodeAccelerator::TFPPicture* -+ VaapiVideoDecodeAccelerator::TFPPictureById(int32 picture_buffer_id) { -+ TFPPictures::iterator it = tfp_pictures_.find(picture_buffer_id); -+ if (it == tfp_pictures_.end()) { -+ DVLOG(1) << "Picture id " << picture_buffer_id << " does not exist"; -+ return NULL; -+ } -+ -+ return it->second.get(); -+} -+ -+VaapiVideoDecodeAccelerator::VaapiVideoDecodeAccelerator( -+ EGLDisplay egl_display, EGLContext egl_context, -+ Client* client, -+ const base::Callback& make_context_current) -+ : x_display_(0), -+ egl_display_(egl_display), -+ egl_context_(egl_context), -+ make_context_current_(make_context_current), -+ state_(kUninitialized), -+ input_ready_(&lock_), -+ surfaces_available_(&lock_), -+ message_loop_(base::MessageLoop::current()), -+ weak_this_(base::AsWeakPtr(this)), -+ client_ptr_factory_(client), -+ client_(client_ptr_factory_.GetWeakPtr()), -+ decoder_thread_("VaapiDecoderThread"), -+ num_frames_at_client_(0), -+ num_stream_bufs_at_decoder_(0), -+ finish_flush_pending_(false), -+ awaiting_va_surfaces_recycle_(false), -+ requested_num_pics_(0) { -+ DCHECK(client); -+} -+VaapiVideoDecodeAccelerator::~VaapiVideoDecodeAccelerator() { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+} -+ -+class ScopedPtrXFree { -+ public: -+ void operator()(void* x) const { -+ ::XFree(x); -+ } -+}; -+ -+bool VaapiVideoDecodeAccelerator::Initialize( -+ media::VideoCodecProfile profile) { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ -+ base::AutoLock auto_lock(lock_); -+ DCHECK_EQ(state_, kUninitialized); -+ DVLOG(2) << "Initializing VAVDA, profile: " << profile; -+ -+ if (!make_context_current_.Run()) -+ return false; -+ -+ x_display_ = base::MessagePumpForUI::GetDefaultXDisplay(); -+ -+ vaapi_wrapper_ = VaapiWrapper::Create( -+ profile, x_display_, -+ base::Bind(&ReportToUMA, content::VaapiH264Decoder::VAAPI_ERROR)); -+ -+ if (!vaapi_wrapper_.get()) { -+ DVLOG(1) << "Failed initializing VAAPI"; -+ return false; -+ } -+ -+ decoder_.reset( -+ new VaapiH264Decoder( -+ vaapi_wrapper_.get(), -+ media::BindToCurrentLoop(base::Bind( -+ &VaapiVideoDecodeAccelerator::SurfaceReady, weak_this_)), -+ base::Bind(&ReportToUMA))); -+ -+ CHECK(decoder_thread_.Start()); -+ -+ state_ = kIdle; -+ -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &Client::NotifyInitializeDone, client_)); -+ return true; -+} -+ -+void VaapiVideoDecodeAccelerator::SurfaceReady( -+ int32 input_id, -+ const scoped_refptr& va_surface) { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ DCHECK(!awaiting_va_surfaces_recycle_); -+ -+ // Drop any requests to output if we are resetting or being destroyed. -+ if (state_ == kResetting || state_ == kDestroying) -+ return; -+ -+ pending_output_cbs_.push( -+ base::Bind(&VaapiVideoDecodeAccelerator::OutputPicture, -+ weak_this_, va_surface, input_id)); -+ -+ TryOutputSurface(); -+} -+ -+void VaapiVideoDecodeAccelerator::OutputPicture( -+ const scoped_refptr& va_surface, -+ int32 input_id, -+ TFPPicture* tfp_picture) { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ -+ int32 output_id = tfp_picture->picture_buffer_id(); -+ -+ TRACE_EVENT2("Video Decoder", "VAVDA::OutputSurface", -+ "input_id", input_id, -+ "output_id", output_id); -+ -+ DVLOG(3) << "Outputting VASurface " << va_surface->id() -+ << " into pixmap bound to picture buffer id " << output_id; -+ -+ RETURN_AND_NOTIFY_ON_FAILURE(tfp_picture->Bind(), -+ "Failed binding texture to pixmap", -+ PLATFORM_FAILURE, ); -+ -+ RETURN_AND_NOTIFY_ON_FAILURE( -+ vaapi_wrapper_->PutSurfaceIntoPixmap(va_surface->id(), -+ tfp_picture->x_pixmap(), -+ tfp_picture->size()), -+ "Failed putting surface into pixmap", PLATFORM_FAILURE, ); -+ -+ // Notify the client a picture is ready to be displayed. -+ ++num_frames_at_client_; -+ TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_); -+ DVLOG(4) << "Notifying output picture id " << output_id -+ << " for input "<< input_id << " is ready"; -+ client_->PictureReady(media::Picture(output_id, input_id)); -+} -+ -+void VaapiVideoDecodeAccelerator::TryOutputSurface() { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ -+ // Handle Destroy() arriving while pictures are queued for output. -+ if (!client_) -+ return; -+ -+ if (pending_output_cbs_.empty() || output_buffers_.empty()) -+ return; -+ -+ OutputCB output_cb = pending_output_cbs_.front(); -+ pending_output_cbs_.pop(); -+ -+ TFPPicture* tfp_picture = TFPPictureById(output_buffers_.front()); -+ DCHECK(tfp_picture); -+ output_buffers_.pop(); -+ -+ output_cb.Run(tfp_picture); -+ -+ if (finish_flush_pending_ && pending_output_cbs_.empty()) -+ FinishFlush(); -+} -+ -+void VaapiVideoDecodeAccelerator::MapAndQueueNewInputBuffer( -+ const media::BitstreamBuffer& bitstream_buffer) { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ TRACE_EVENT1("Video Decoder", "MapAndQueueNewInputBuffer", "input_id", -+ bitstream_buffer.id()); -+ -+ DVLOG(4) << "Mapping new input buffer id: " << bitstream_buffer.id() -+ << " size: " << (int)bitstream_buffer.size(); -+ -+ scoped_ptr shm( -+ new base::SharedMemory(bitstream_buffer.handle(), true)); -+ RETURN_AND_NOTIFY_ON_FAILURE(shm->Map(bitstream_buffer.size()), -+ "Failed to map input buffer", UNREADABLE_INPUT,); -+ -+ base::AutoLock auto_lock(lock_); -+ -+ // Set up a new input buffer and queue it for later. -+ linked_ptr input_buffer(new InputBuffer()); -+ input_buffer->shm.reset(shm.release()); -+ input_buffer->id = bitstream_buffer.id(); -+ input_buffer->size = bitstream_buffer.size(); -+ -+ ++num_stream_bufs_at_decoder_; -+ TRACE_COUNTER1("Video Decoder", "Stream buffers at decoder", -+ num_stream_bufs_at_decoder_); -+ -+ input_buffers_.push(input_buffer); -+ input_ready_.Signal(); -+} -+ -+bool VaapiVideoDecodeAccelerator::GetInputBuffer_Locked() { -+ DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current()); -+ lock_.AssertAcquired(); -+ -+ if (curr_input_buffer_.get()) -+ return true; -+ -+ // Will only wait if it is expected that in current state new buffers will -+ // be queued from the client via Decode(). The state can change during wait. -+ while (input_buffers_.empty() && (state_ == kDecoding || state_ == kIdle)) { -+ input_ready_.Wait(); -+ } -+ -+ // We could have got woken up in a different state or never got to sleep -+ // due to current state; check for that. -+ switch (state_) { -+ case kFlushing: -+ // Here we are only interested in finishing up decoding buffers that are -+ // already queued up. Otherwise will stop decoding. -+ if (input_buffers_.empty()) -+ return false; -+ // else fallthrough -+ case kDecoding: -+ case kIdle: -+ DCHECK(!input_buffers_.empty()); -+ -+ curr_input_buffer_ = input_buffers_.front(); -+ input_buffers_.pop(); -+ -+ DVLOG(4) << "New current bitstream buffer, id: " -+ << curr_input_buffer_->id -+ << " size: " << curr_input_buffer_->size; -+ -+ decoder_->SetStream( -+ static_cast(curr_input_buffer_->shm->memory()), -+ curr_input_buffer_->size, curr_input_buffer_->id); -+ return true; -+ -+ default: -+ // We got woken up due to being destroyed/reset, ignore any already -+ // queued inputs. -+ return false; -+ } -+} -+ -+void VaapiVideoDecodeAccelerator::ReturnCurrInputBuffer_Locked() { -+ lock_.AssertAcquired(); -+ DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current()); -+ DCHECK(curr_input_buffer_.get()); -+ -+ int32 id = curr_input_buffer_->id; -+ curr_input_buffer_.reset(); -+ DVLOG(4) << "End of input buffer " << id; -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &Client::NotifyEndOfBitstreamBuffer, client_, id)); -+ -+ --num_stream_bufs_at_decoder_; -+ TRACE_COUNTER1("Video Decoder", "Stream buffers at decoder", -+ num_stream_bufs_at_decoder_); -+} -+ -+bool VaapiVideoDecodeAccelerator::FeedDecoderWithOutputSurfaces_Locked() { -+ lock_.AssertAcquired(); -+ DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current()); -+ -+ while (available_va_surfaces_.empty() && -+ (state_ == kDecoding || state_ == kFlushing || state_ == kIdle)) { -+ surfaces_available_.Wait(); -+ } -+ -+ if (state_ != kDecoding && state_ != kFlushing && state_ != kIdle) -+ return false; -+ -+ VASurface::ReleaseCB va_surface_release_cb = -+ media::BindToCurrentLoop(base::Bind( -+ &VaapiVideoDecodeAccelerator::RecycleVASurfaceID, weak_this_)); -+ -+ while (!available_va_surfaces_.empty()) { -+ scoped_refptr va_surface( -+ new VASurface(available_va_surfaces_.front(), va_surface_release_cb)); -+ available_va_surfaces_.pop_front(); -+ decoder_->ReuseSurface(va_surface); -+ } -+ -+ return true; -+} -+ -+void VaapiVideoDecodeAccelerator::DecodeTask() { -+ DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current()); -+ TRACE_EVENT0("Video Decoder", "VAVDA::DecodeTask"); -+ base::AutoLock auto_lock(lock_); -+ -+ if (state_ != kDecoding) -+ return; -+ -+ // Main decode task. -+ DVLOG(4) << "Decode task"; -+ -+ // Try to decode what stream data is (still) in the decoder until we run out -+ // of it. -+ while (GetInputBuffer_Locked()) { -+ DCHECK(curr_input_buffer_.get()); -+ -+ VaapiH264Decoder::DecResult res; -+ { -+ // We are OK releasing the lock here, as decoder never calls our methods -+ // directly and we will reacquire the lock before looking at state again. -+ // This is the main decode function of the decoder and while keeping -+ // the lock for its duration would be fine, it would defeat the purpose -+ // of having a separate decoder thread. -+ base::AutoUnlock auto_unlock(lock_); -+ res = decoder_->Decode(); -+ } -+ -+ switch (res) { -+ case VaapiH264Decoder::kAllocateNewSurfaces: -+ DVLOG(1) << "Decoder requesting a new set of surfaces"; -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange, weak_this_, -+ decoder_->GetRequiredNumOfPictures(), -+ decoder_->GetPicSize())); -+ // We'll get rescheduled once ProvidePictureBuffers() finishes. -+ return; -+ -+ case VaapiH264Decoder::kRanOutOfStreamData: -+ ReturnCurrInputBuffer_Locked(); -+ break; -+ -+ case VaapiH264Decoder::kRanOutOfSurfaces: -+ // No more output buffers in the decoder, try getting more or go to -+ // sleep waiting for them. -+ if (!FeedDecoderWithOutputSurfaces_Locked()) -+ return; -+ -+ break; -+ -+ case VaapiH264Decoder::kDecodeError: -+ RETURN_AND_NOTIFY_ON_FAILURE(false, "Error decoding stream", -+ PLATFORM_FAILURE, ); -+ return; -+ } -+ } -+} -+ -+void VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange(size_t num_pics, -+ gfx::Size size) { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ DCHECK(!awaiting_va_surfaces_recycle_); -+ -+ // At this point decoder has stopped running and has already posted onto our -+ // loop any remaining output request callbacks, which executed before we got -+ // here. Some of them might have been pended though, because we might not -+ // have had enough TFPictures to output surfaces to. Initiate a wait cycle, -+ // which will wait for client to return enough PictureBuffers to us, so that -+ // we can finish all pending output callbacks, releasing associated surfaces. -+ DVLOG(1) << "Initiating surface set change"; -+ awaiting_va_surfaces_recycle_ = true; -+ -+ requested_num_pics_ = num_pics; -+ requested_pic_size_ = size; -+ -+ TryFinishSurfaceSetChange(); -+} -+ -+void VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange() { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ -+ if (!awaiting_va_surfaces_recycle_) -+ return; -+ -+ if (!pending_output_cbs_.empty() || -+ tfp_pictures_.size() != available_va_surfaces_.size()) { -+ // Either: -+ // 1. Not all pending pending output callbacks have been executed yet. -+ // Wait for the client to return enough pictures and retry later. -+ // 2. The above happened and all surface release callbacks have been posted -+ // as the result, but not all have executed yet. Post ourselves after them -+ // to let them release surfaces. -+ DVLOG(2) << "Awaiting pending output/surface release callbacks to finish"; -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange, weak_this_)); -+ return; -+ } -+ -+ // All surfaces released, destroy them and dismiss all PictureBuffers. -+ awaiting_va_surfaces_recycle_ = false; -+ available_va_surfaces_.clear(); -+ vaapi_wrapper_->DestroySurfaces(); -+ -+ for (TFPPictures::iterator iter = tfp_pictures_.begin(); -+ iter != tfp_pictures_.end(); ++iter) { -+ DVLOG(2) << "Dismissing picture id: " << iter->first; -+ client_->DismissPictureBuffer(iter->first); -+ } -+ tfp_pictures_.clear(); -+ -+ // And ask for a new set as requested. -+ DVLOG(1) << "Requesting " << requested_num_pics_ << " pictures of size: " -+ << requested_pic_size_.ToString(); -+ -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &Client::ProvidePictureBuffers, client_, -+ requested_num_pics_, requested_pic_size_, GL_TEXTURE_2D)); -+} -+ -+void VaapiVideoDecodeAccelerator::Decode( -+ const media::BitstreamBuffer& bitstream_buffer) { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ -+ TRACE_EVENT1("Video Decoder", "VAVDA::Decode", "Buffer id", -+ bitstream_buffer.id()); -+ -+ // We got a new input buffer from the client, map it and queue for later use. -+ MapAndQueueNewInputBuffer(bitstream_buffer); -+ -+ base::AutoLock auto_lock(lock_); -+ switch (state_) { -+ case kIdle: -+ state_ = kDecoding; -+ decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::DecodeTask, -+ base::Unretained(this))); -+ break; -+ -+ case kDecoding: -+ // Decoder already running. -+ case kResetting: -+ // When resetting, allow accumulating bitstream buffers, so that -+ // the client can queue after-seek-buffers while we are finishing with -+ // the before-seek one. -+ break; -+ -+ default: -+ RETURN_AND_NOTIFY_ON_FAILURE(false, -+ "Decode request from client in invalid state: " << state_, -+ PLATFORM_FAILURE, ); -+ break; -+ } -+} -+ -+void VaapiVideoDecodeAccelerator::RecycleVASurfaceID( -+ VASurfaceID va_surface_id) { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ base::AutoLock auto_lock(lock_); -+ -+ available_va_surfaces_.push_back(va_surface_id); -+ surfaces_available_.Signal(); -+} -+ -+void VaapiVideoDecodeAccelerator::AssignPictureBuffers( -+ const std::vector& buffers) { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ -+ base::AutoLock auto_lock(lock_); -+ DCHECK(tfp_pictures_.empty()); -+ -+ while (!output_buffers_.empty()) -+ output_buffers_.pop(); -+ -+ RETURN_AND_NOTIFY_ON_FAILURE( -+ buffers.size() == requested_num_pics_, -+ "Got an invalid number of picture buffers. (Got " << buffers.size() -+ << ", requested " << requested_num_pics_ << ")", INVALID_ARGUMENT, ); -+ DCHECK(requested_pic_size_ == buffers[0].size()); -+ -+ std::vector va_surface_ids; -+ RETURN_AND_NOTIFY_ON_FAILURE( -+ vaapi_wrapper_->CreateSurfaces(requested_pic_size_, -+ buffers.size(), -+ &va_surface_ids), -+ "Failed creating VA Surfaces", PLATFORM_FAILURE, ); -+ DCHECK_EQ(va_surface_ids.size(), buffers.size()); -+ -+ for (size_t i = 0; i < buffers.size(); ++i) { -+ DVLOG(2) << "Assigning picture id: " << buffers[i].id() -+ << " to texture id: " << buffers[i].texture_id() -+ << " VASurfaceID: " << va_surface_ids[i]; -+ -+ linked_ptr tfp_picture( -+ TFPPicture::Create(make_context_current_, egl_display_, x_display_, -+ buffers[i].id(), buffers[i].texture_id(), -+ requested_pic_size_)); -+ -+ RETURN_AND_NOTIFY_ON_FAILURE( -+ tfp_picture.get(), "Failed assigning picture buffer to a texture.", -+ PLATFORM_FAILURE, ); -+ -+ bool inserted = tfp_pictures_.insert(std::make_pair( -+ buffers[i].id(), tfp_picture)).second; -+ DCHECK(inserted); -+ -+ output_buffers_.push(buffers[i].id()); -+ available_va_surfaces_.push_back(va_surface_ids[i]); -+ surfaces_available_.Signal(); -+ } -+ -+ state_ = kDecoding; -+ decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::DecodeTask, base::Unretained(this))); -+} -+ -+void VaapiVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id) { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ TRACE_EVENT1("Video Decoder", "VAVDA::ReusePictureBuffer", "Picture id", -+ picture_buffer_id); -+ -+ --num_frames_at_client_; -+ TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_); -+ -+ output_buffers_.push(picture_buffer_id); -+ TryOutputSurface(); -+} -+ -+void VaapiVideoDecodeAccelerator::FlushTask() { -+ DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current()); -+ DVLOG(1) << "Flush task"; -+ -+ // First flush all the pictures that haven't been outputted, notifying the -+ // client to output them. -+ bool res = decoder_->Flush(); -+ RETURN_AND_NOTIFY_ON_FAILURE(res, "Failed flushing the decoder.", -+ PLATFORM_FAILURE, ); -+ -+ // Put the decoder in idle state, ready to resume. -+ decoder_->Reset(); -+ -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::FinishFlush, weak_this_)); -+} -+ -+void VaapiVideoDecodeAccelerator::Flush() { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ DVLOG(1) << "Got flush request"; -+ -+ base::AutoLock auto_lock(lock_); -+ state_ = kFlushing; -+ // Queue a flush task after all existing decoding tasks to clean up. -+ decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::FlushTask, base::Unretained(this))); -+ -+ input_ready_.Signal(); -+ surfaces_available_.Signal(); -+} -+ -+void VaapiVideoDecodeAccelerator::FinishFlush() { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ -+ finish_flush_pending_ = false; -+ -+ base::AutoLock auto_lock(lock_); -+ if (state_ != kFlushing) { -+ DCHECK_EQ(state_, kDestroying); -+ return; // We could've gotten destroyed already. -+ } -+ -+ // Still waiting for textures from client to finish outputting all pending -+ // frames. Try again later. -+ if (!pending_output_cbs_.empty()) { -+ finish_flush_pending_ = true; -+ return; -+ } -+ -+ state_ = kIdle; -+ -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &Client::NotifyFlushDone, client_)); -+ -+ DVLOG(1) << "Flush finished"; -+} -+ -+void VaapiVideoDecodeAccelerator::ResetTask() { -+ DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current()); -+ DVLOG(1) << "ResetTask"; -+ -+ // All the decoding tasks from before the reset request from client are done -+ // by now, as this task was scheduled after them and client is expected not -+ // to call Decode() after Reset() and before NotifyResetDone. -+ decoder_->Reset(); -+ -+ base::AutoLock auto_lock(lock_); -+ -+ // Return current input buffer, if present. -+ if (curr_input_buffer_.get()) -+ ReturnCurrInputBuffer_Locked(); -+ -+ // And let client know that we are done with reset. -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::FinishReset, weak_this_)); -+} -+ -+void VaapiVideoDecodeAccelerator::Reset() { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ DVLOG(1) << "Got reset request"; -+ -+ // This will make any new decode tasks exit early. -+ base::AutoLock auto_lock(lock_); -+ state_ = kResetting; -+ finish_flush_pending_ = false; -+ -+ // Drop all remaining input buffers, if present. -+ while (!input_buffers_.empty()) { -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &Client::NotifyEndOfBitstreamBuffer, client_, -+ input_buffers_.front()->id)); -+ input_buffers_.pop(); -+ } -+ -+ decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::ResetTask, base::Unretained(this))); -+ -+ input_ready_.Signal(); -+ surfaces_available_.Signal(); -+} -+ -+void VaapiVideoDecodeAccelerator::FinishReset() { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ DVLOG(1) << "FinishReset"; -+ base::AutoLock auto_lock(lock_); -+ -+ if (state_ != kResetting) { -+ DCHECK(state_ == kDestroying || state_ == kUninitialized) << state_; -+ return; // We could've gotten destroyed already. -+ } -+ -+ // Drop pending outputs. -+ while (!pending_output_cbs_.empty()) -+ pending_output_cbs_.pop(); -+ -+ if (awaiting_va_surfaces_recycle_) { -+ // Decoder requested a new surface set while we were waiting for it to -+ // finish the last DecodeTask, running at the time of Reset(). -+ // Let the surface set change finish first before resetting. -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::FinishReset, weak_this_)); -+ return; -+ } -+ -+ num_stream_bufs_at_decoder_ = 0; -+ state_ = kIdle; -+ -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &Client::NotifyResetDone, client_)); -+ -+ // The client might have given us new buffers via Decode() while we were -+ // resetting and might be waiting for our move, and not call Decode() anymore -+ // until we return something. Post a DecodeTask() so that we won't -+ // sleep forever waiting for Decode() in that case. Having two of them -+ // in the pipe is harmless, the additional one will return as soon as it sees -+ // that we are back in kDecoding state. -+ if (!input_buffers_.empty()) { -+ state_ = kDecoding; -+ decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::DecodeTask, -+ base::Unretained(this))); -+ } -+ -+ DVLOG(1) << "Reset finished"; -+} -+ -+void VaapiVideoDecodeAccelerator::Cleanup() { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ -+ if (state_ == kUninitialized || state_ == kDestroying) -+ return; -+ -+ DVLOG(1) << "Destroying VAVDA"; -+ base::AutoLock auto_lock(lock_); -+ state_ = kDestroying; -+ -+ client_ptr_factory_.InvalidateWeakPtrs(); -+ -+ { -+ base::AutoUnlock auto_unlock(lock_); -+ // Post a dummy task to the decoder_thread_ to ensure it is drained. -+ base::WaitableEvent waiter(false, false); -+ decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( -+ &base::WaitableEvent::Signal, base::Unretained(&waiter))); -+ input_ready_.Signal(); -+ surfaces_available_.Signal(); -+ waiter.Wait(); -+ decoder_thread_.Stop(); -+ } -+ -+ state_ = kUninitialized; -+} -+ -+void VaapiVideoDecodeAccelerator::Destroy() { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ Cleanup(); -+ delete this; -+} -+ -+} // namespace content -diff --git a/content/common/gpu/media/vaapi_video_decode_accelerator_tizen.h b/content/common/gpu/media/vaapi_video_decode_accelerator_tizen.h -new file mode 100644 -index 0000000..d41cf38 ---- /dev/null -+++ b/content/common/gpu/media/vaapi_video_decode_accelerator_tizen.h -@@ -0,0 +1,273 @@ -+// Copyright (c) 2013 Intel Corporation. All rights reserved. -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+// -+// This file contains an implementation of VideoDecoderAccelerator -+// that utilizes hardware video decoder present on Intel CPUs for Tizen. -+ -+#ifndef CONTENT_COMMON_GPU_MEDIA_VAAPI_VIDEO_DECODE_ACCELERATOR_H_ -+#define CONTENT_COMMON_GPU_MEDIA_VAAPI_VIDEO_DECODE_ACCELERATOR_H_ -+ -+#include -+#include -+#include -+#include -+ -+#include "base/logging.h" -+#include "base/memory/linked_ptr.h" -+#include "base/memory/shared_memory.h" -+#include "base/memory/weak_ptr.h" -+#include "base/message_loop/message_loop.h" -+#include "base/synchronization/condition_variable.h" -+#include "base/synchronization/lock.h" -+#include "base/threading/non_thread_safe.h" -+#include "base/threading/thread.h" -+#include "content/common/content_export.h" -+#include "content/common/gpu/media/vaapi_h264_decoder.h" -+#include "content/common/gpu/media/vaapi_wrapper.h" -+#include "content/common/gpu/media/video_decode_accelerator_impl.h" -+#include "media/base/bitstream_buffer.h" -+#include "media/video/picture.h" -+#include "media/video/video_decode_accelerator.h" -+#include "ui/gl/gl_bindings.h" -+ -+namespace content { -+ -+// Class to provide video decode acceleration for Intel systems with hardware -+// support for it, and on which libva is available. -+// Decoding tasks are performed in a separate decoding thread. -+// -+// Threading/life-cycle: this object is created & destroyed on the GPU -+// ChildThread. A few methods on it are called on the decoder thread which is -+// stopped during |this->Destroy()|, so any tasks posted to the decoder thread -+// can assume |*this| is still alive. See |weak_this_| below for more details. -+class CONTENT_EXPORT VaapiVideoDecodeAccelerator -+ : public VideoDecodeAcceleratorImpl { -+ public: -+ VaapiVideoDecodeAccelerator( -+ EGLDisplay egl_display, EGLContext egl_context, -+ Client* client, -+ const base::Callback& make_context_current); -+ virtual ~VaapiVideoDecodeAccelerator(); -+ -+ // media::VideoDecodeAccelerator implementation. -+ virtual bool Initialize(media::VideoCodecProfile profile) OVERRIDE; -+ virtual void Decode(const media::BitstreamBuffer& bitstream_buffer) OVERRIDE; -+ virtual void AssignPictureBuffers( -+ const std::vector& buffers) OVERRIDE; -+ virtual void ReusePictureBuffer(int32 picture_buffer_id) OVERRIDE; -+ virtual void Flush() OVERRIDE; -+ virtual void Reset() OVERRIDE; -+ virtual void Destroy() OVERRIDE; -+ -+private: -+ // Notify the client that |output_id| is ready for displaying. -+ void NotifyPictureReady(int32 input_id, int32 output_id); -+ -+ // Notify the client that an error has occurred and decoding cannot continue. -+ void NotifyError(Error error); -+ -+ // Map the received input buffer into this process' address space and -+ // queue it for decode. -+ void MapAndQueueNewInputBuffer( -+ const media::BitstreamBuffer& bitstream_buffer); -+ -+ // Get a new input buffer from the queue and set it up in decoder. This will -+ // sleep if no input buffers are available. Return true if a new buffer has -+ // been set up, false if an early exit has been requested (due to initiated -+ // reset/flush/destroy). -+ bool GetInputBuffer_Locked(); -+ -+ // Signal the client that the current buffer has been read and can be -+ // returned. Will also release the mapping. -+ void ReturnCurrInputBuffer_Locked(); -+ -+ // Pass one or more output buffers to the decoder. This will sleep -+ // if no buffers are available. Return true if buffers have been set up or -+ // false if an early exit has been requested (due to initiated -+ // reset/flush/destroy). -+ bool FeedDecoderWithOutputSurfaces_Locked(); -+ -+ // Continue decoding given input buffers and sleep waiting for input/output -+ // as needed. Will exit if a new set of surfaces or reset/flush/destroy -+ // is requested. -+ void DecodeTask(); -+ -+ // Scheduled after receiving a flush request and executed after the current -+ // decoding task finishes decoding pending inputs. Makes the decoder return -+ // all remaining output pictures and puts it in an idle state, ready -+ // to resume if needed and schedules a FinishFlush. -+ void FlushTask(); -+ -+ // Scheduled by the FlushTask after decoder is flushed to put VAVDA into idle -+ // state and notify the client that flushing has been finished. -+ void FinishFlush(); -+ -+ // Scheduled after receiving a reset request and executed after the current -+ // decoding task finishes decoding the current frame. Puts the decoder into -+ // an idle state, ready to resume if needed, discarding decoded but not yet -+ // outputted pictures (decoder keeps ownership of their associated picture -+ // buffers). Schedules a FinishReset afterwards. -+ void ResetTask(); -+ -+ // Scheduled by ResetTask after it's done putting VAVDA into an idle state. -+ // Drops remaining input buffers and notifies the client that reset has been -+ // finished. -+ void FinishReset(); -+ -+ // Helper for Destroy(), doing all the actual work except for deleting self. -+ void Cleanup(); -+ -+ // Get a usable framebuffer configuration for use in binding textures -+ // or return false on failure. -+ bool InitializeFBConfig(); -+ -+ // Callback for the decoder to execute when it wants us to output given -+ // |va_surface|. -+ void SurfaceReady(int32 input_id, const scoped_refptr& va_surface); -+ -+ // Represents a texture bound to an X Pixmap for output purposes. -+ class TFPPicture; -+ -+ // Callback to be executed once we have a |va_surface| to be output and -+ // an available |tfp_picture| to use for output. -+ // Puts contents of |va_surface| into given |tfp_picture|, releases the -+ // surface and passes the resulting picture to client for output. -+ void OutputPicture(const scoped_refptr& va_surface, -+ int32 input_id, -+ TFPPicture* tfp_picture); -+ -+ // Try to OutputPicture() if we have both a ready surface and picture. -+ void TryOutputSurface(); -+ -+ // Called when a VASurface is no longer in use by the decoder or is not being -+ // synced/waiting to be synced to a picture. Returns it to available surfaces -+ // pool. -+ void RecycleVASurfaceID(VASurfaceID va_surface_id); -+ -+ // Initiate wait cycle for surfaces to be released before we release them -+ // and allocate new ones, as requested by the decoder. -+ void InitiateSurfaceSetChange(size_t num_pics, gfx::Size size); -+ // Check if the surfaces have been released or post ourselves for later. -+ void TryFinishSurfaceSetChange(); -+ -+ // Client-provided X/EGL state. -+ Display* x_display_; -+ EGLDisplay egl_display_; -+ EGLContext egl_context_; -+ base::Callback make_context_current_; -+ -+ // VAVDA state. -+ enum State { -+ // Initialize() not called yet or failed. -+ kUninitialized, -+ // DecodeTask running. -+ kDecoding, -+ // Resetting, waiting for decoder to finish current task and cleanup. -+ kResetting, -+ // Flushing, waiting for decoder to finish current task and cleanup. -+ kFlushing, -+ // Idle, decoder in state ready to start/resume decoding. -+ kIdle, -+ // Destroying, waiting for the decoder to finish current task. -+ kDestroying, -+ }; -+ -+ // Protects input buffer and surface queues and state_. -+ base::Lock lock_; -+ State state_; -+ -+ // An input buffer awaiting consumption, provided by the client. -+ struct InputBuffer { -+ InputBuffer(); -+ ~InputBuffer(); -+ -+ int32 id; -+ size_t size; -+ scoped_ptr shm; -+ }; -+ -+ // Queue for incoming input buffers. -+ typedef std::queue > InputBuffers; -+ InputBuffers input_buffers_; -+ // Signalled when input buffers are queued onto the input_buffers_ queue. -+ base::ConditionVariable input_ready_; -+ -+ // Current input buffer at decoder. -+ linked_ptr curr_input_buffer_; -+ -+ // Queue for incoming output buffers (texture ids). -+ typedef std::queue OutputBuffers; -+ OutputBuffers output_buffers_; -+ -+ typedef std::map > TFPPictures; -+ // All allocated TFPPictures, regardless of their current state. TFPPictures -+ // are allocated once and destroyed at the end of decode. -+ TFPPictures tfp_pictures_; -+ -+ // Return a TFPPicture associated with given client-provided id. -+ TFPPicture* TFPPictureById(int32 picture_buffer_id); -+ -+ // VA Surfaces no longer in use that can be passed back to the decoder for -+ // reuse, once it requests them. -+ std::list available_va_surfaces_; -+ // Signalled when output surfaces are queued onto the available_va_surfaces_ -+ // queue. -+ base::ConditionVariable surfaces_available_; -+ -+ // Pending output requests from the decoder. When it indicates that we should -+ // output a surface and we have an available TFPPicture (i.e. texture) ready -+ // to use, we'll execute the callback passing the TFPPicture. The callback -+ // will put the contents of the surface into the picture and return it to -+ // the client, releasing the surface as well. -+ // If we don't have any available TFPPictures at the time when the decoder -+ // requests output, we'll store the request on pending_output_cbs_ queue for -+ // later and run it once the client gives us more textures -+ // via ReusePictureBuffer(). -+ typedef base::Callback OutputCB; -+ std::queue pending_output_cbs_; -+ -+ // ChildThread's message loop -+ base::MessageLoop* message_loop_; -+ -+ // WeakPtr<> pointing to |this| for use in posting tasks from the decoder -+ // thread back to the ChildThread. Because the decoder thread is a member of -+ // this class, any task running on the decoder thread is guaranteed that this -+ // object is still alive. As a result, tasks posted from ChildThread to -+ // decoder thread should use base::Unretained(this), and tasks posted from the -+ // decoder thread to the ChildThread should use |weak_this_|. -+ base::WeakPtr weak_this_; -+ -+ // To expose client callbacks from VideoDecodeAccelerator. -+ // NOTE: all calls to these objects *MUST* be executed on message_loop_. -+ base::WeakPtrFactory client_ptr_factory_; -+ base::WeakPtr client_; -+ -+ scoped_ptr vaapi_wrapper_; -+ -+ // Comes after vaapi_wrapper_ to ensure its destructor is executed before -+ // vaapi_wrapper_ is destroyed. -+ scoped_ptr decoder_; -+ base::Thread decoder_thread_; -+ -+ int num_frames_at_client_; -+ int num_stream_bufs_at_decoder_; -+ -+ // Whether we are waiting for any pending_output_cbs_ to be run before -+ // NotifyingFlushDone. -+ bool finish_flush_pending_; -+ -+ // Decoder requested a new surface set and we are waiting for all the surfaces -+ // to be returned before we can free them. -+ bool awaiting_va_surfaces_recycle_; -+ -+ // Last requested number/resolution of output picture buffers. -+ size_t requested_num_pics_; -+ gfx::Size requested_pic_size_; -+ -+ DISALLOW_COPY_AND_ASSIGN(VaapiVideoDecodeAccelerator); -+}; -+ -+} // namespace content -+ -+#endif // CONTENT_COMMON_GPU_MEDIA_VAAPI_VIDEO_DECODE_ACCELERATOR_H_ -diff --git a/content/content_common.gypi b/content/content_common.gypi -index 9d6cb61..3f53dd5 100644 ---- a/content/content_common.gypi -+++ b/content/content_common.gypi -@@ -583,6 +583,32 @@ - '<(DEPTH)/third_party/libva', - ], - }], -+ ['target_arch != "arm" and tizen_mobile == 1 and use_x11 == 1', { -+ 'dependencies': [ -+ '../media/media.gyp:media', -+ ], -+ 'sources': [ -+ 'common/gpu/media/h264_dpb.cc', -+ 'common/gpu/media/h264_dpb.h', -+ 'common/gpu/media/va_surface.h', -+ 'common/gpu/media/vaapi_h264_decoder.cc', -+ 'common/gpu/media/vaapi_h264_decoder.h', -+ 'common/gpu/media/vaapi_video_decode_accelerator_tizen.cc', -+ 'common/gpu/media/vaapi_video_decode_accelerator_tizen.h', -+ 'common/gpu/media/vaapi_wrapper.cc', -+ 'common/gpu/media/vaapi_wrapper.h', -+ ], -+ 'include_dirs': [ -+ '<(DEPTH)/third_party/libva', -+ '<(DEPTH)/third_party/khronos', -+ ], -+ 'link_settings': { -+ 'libraries': [ -+ '-lEGL', -+ '-lGLESv2', -+ ], -+ }, -+ }], - ['OS=="win"', { - 'dependencies': [ - '../media/media.gyp:media', -diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc -index 9e29e03..2c04d40 100644 ---- a/content/gpu/gpu_main.cc -+++ b/content/gpu/gpu_main.cc -@@ -42,7 +42,8 @@ - #include "sandbox/win/src/sandbox.h" - #elif defined(OS_CHROMEOS) && defined(ARCH_CPU_ARMEL) && defined(USE_X11) - #include "content/common/gpu/media/exynos_video_decode_accelerator.h" --#elif defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY) && defined(USE_X11) -+#elif (defined(OS_CHROMEOS) || defined(OS_TIZEN_MOBILE)) &&\ -+ defined(ARCH_CPU_X86_FAMILY) && defined(USE_X11) - #include "content/common/gpu/media/vaapi_wrapper.h" - #endif - -@@ -360,7 +361,8 @@ bool WarmUpSandbox(const CommandLine& command_line) { - - #if defined(OS_CHROMEOS) && defined(ARCH_CPU_ARMEL) && defined(USE_X11) - ExynosVideoDecodeAccelerator::PreSandboxInitialization(); --#elif defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY) && defined(USE_X11) -+#elif (defined(OS_CHROMEOS) || defined(OS_TIZEN_MOBILE)) &&\ -+ defined(ARCH_CPU_X86_FAMILY) && defined(USE_X11) - VaapiWrapper::PreSandboxInitialization(); - #endif - --- -1.8.3.2 - diff --git a/packaging/crosswalk.spec b/packaging/crosswalk.spec index 3e2ed89..5a327fd 100644 --- a/packaging/crosswalk.spec +++ b/packaging/crosswalk.spec @@ -2,7 +2,7 @@ %bcond_with wayland Name: crosswalk -Version: 5.34.105.0 +Version: 6.34.113.0 Release: 0 Summary: Crosswalk is an app runtime based on Chromium License: (BSD-3-Clause and LGPL-2.1+) @@ -16,7 +16,6 @@ Source1001: crosswalk.manifest Source1002: %{name}.xml.in Source1003: %{name}.png Patch1: %{name}-do-not-look-for-gtk2-when-using-aura.patch -Patch7: %{name}-tizen-audio-session-manager.patch Patch8: %{name}-mesa-ozone-typedefs.patch Patch9: Blink-Add-GCC-flag-Wno-narrowing-fix-64bits-build.patch @@ -26,13 +25,13 @@ BuildRequires: expat-devel BuildRequires: flex BuildRequires: gperf BuildRequires: libcap-devel +BuildRequires: ninja BuildRequires: python BuildRequires: python-xml BuildRequires: perl BuildRequires: which BuildRequires: pkgconfig(alsa) BuildRequires: pkgconfig(appcore-common) -BuildRequires: pkgconfig(audio-session-mgr) BuildRequires: pkgconfig(cairo) BuildRequires: pkgconfig(capi-location-manager) BuildRequires: pkgconfig(dbus-1) @@ -107,7 +106,6 @@ cp -a src/LICENSE LICENSE.chromium cp -a src/xwalk/LICENSE LICENSE.xwalk %patch1 -%patch7 %if "%{tizen}" < "3.0" %patch2 @@ -143,34 +141,20 @@ export LDFLAGS="${LDFLAGS} -Wl,--no-keep-memory" # build root to the BUILDDIR_NAME definition, such as "/var/tmp/xwalk-build" # (remember all paths are still inside the chroot): # gbs build --define 'BUILDDIR_NAME /some/path' -# -# The --depth and --generator-output combo is used to put all the Makefiles -# inside the build directory, and (this is the important part) keep file lists -# (generatedwith <|() in gyp) in the build directory as well, otherwise they -# will be in the source directory, erased every time and trigger an almost full -# Blink rebuild (among other smaller targets). -# We cannot always pass those flags, though, because gyp's make generator does -# not work if the --generator-output is the top-level source directory. BUILDDIR_NAME="%{?BUILDDIR_NAME}" -if [ -z "${BUILDDIR_NAME}" ]; then - BUILDDIR_NAME="." -else - GYP_EXTRA_FLAGS="--depth=. --generator-output=${BUILDDIR_NAME}" +if [ -n "${BUILDDIR_NAME}" ]; then + mkdir -p "${BUILDDIR_NAME}" + ln -s "${BUILDDIR_NAME}" src/out fi %if %{with wayland} -GYP_EXTRA_FLAGS="${GYP_EXTRA_FLAGS} -Duse_ash=1 -Duse_ozone=1" +GYP_EXTRA_FLAGS="${GYP_EXTRA_FLAGS} -Duse_ozone=1" %endif -# Change src/ so that we can pass "." to --depth below, otherwise we would need -# to pass "src" to it, but this confuses the gyp make generator, that expects -# to be called from the root source directory. -cd src - # --no-parallel is added because chroot does not mount a /dev/shm, this will # cause python multiprocessing.SemLock error. -export GYP_GENERATORS='make' -./xwalk/gyp_xwalk xwalk/xwalk.gyp \ +export GYP_GENERATORS='ninja' +./src/xwalk/gyp_xwalk src/xwalk/xwalk.gyp \ --no-parallel \ ${GYP_EXTRA_FLAGS} \ -Dchromeos=0 \ @@ -182,55 +166,32 @@ ${GYP_EXTRA_FLAGS} \ -Duse_gconf=0 \ -Duse_kerberos=0 \ -Duse_system_bzip2=1 \ --Duse_system_icu=1 \ -Duse_system_libexif=1 \ -Duse_system_libxml=1 \ -Duse_system_nspr=1 \ --Denable_xi21_mt=1 \ --Duse_xi2_mt=0 \ -Denable_hidpi=1 -make %{?_smp_mflags} -C "${BUILDDIR_NAME}" BUILDTYPE=Release xwalk xwalkctl xwalk_launcher xwalk-pkg-helper +ninja %{?_smp_mflags} -C src/out/Release xwalk xwalkctl xwalk_launcher xwalk-pkg-helper %install -# Support building in a non-standard directory, possibly outside %{_builddir}. -# Since the build root is erased every time a new build is performed, one way -# to avoid losing the build directory is to specify a location outside the -# build root to the BUILDDIR_NAME definition, such as "/var/tmp/xwalk-build" -# (remember all paths are still inside the chroot): -# gbs build --define 'BUILDDIR_NAME /some/path' -BUILDDIR_NAME="%{?BUILDDIR_NAME}" -if [ -z "${BUILDDIR_NAME}" ]; then - BUILDDIR_NAME="." -fi - -# Since BUILDDIR_NAME can be either a relative path or an absolute one, we need -# to cd into src/ so that it means the same thing in the build and install -# stages: during the former, a relative location refers to a place inside src/, -# whereas during the latter a relative location by default would refer to a -# place one directory above src/. If BUILDDIR_NAME is an absolute path, this is -# irrelevant anyway. -cd src - # Binaries. -install -p -D ../xwalk %{buildroot}%{_bindir}/xwalk +install -p -D xwalk %{buildroot}%{_bindir}/xwalk install -p -D %{SOURCE2} %{buildroot}%{_dbusservicedir}/org.crosswalkproject.Runtime1.service -install -p -D ../xwalk.service %{buildroot}%{_systemduserservicedir}/xwalk.service -install -p -D ${BUILDDIR_NAME}/out/Release/xwalk %{buildroot}%{_libdir}/xwalk/xwalk -install -p -D ${BUILDDIR_NAME}/out/Release/xwalkctl %{buildroot}%{_bindir}/xwalkctl -install -p -D ${BUILDDIR_NAME}/out/Release/xwalk-launcher %{buildroot}%{_bindir}/xwalk-launcher +install -p -D xwalk.service %{buildroot}%{_systemduserservicedir}/xwalk.service +install -p -D src/out/Release/xwalk %{buildroot}%{_libdir}/xwalk/xwalk +install -p -D src/out/Release/xwalkctl %{buildroot}%{_bindir}/xwalkctl +install -p -D src/out/Release/xwalk-launcher %{buildroot}%{_bindir}/xwalk-launcher # xwalk-pkg-helper needs to be set-user-ID-root so it can finish the installation process. -install -m 06755 -p -D ${BUILDDIR_NAME}/out/Release/xwalk-pkg-helper %{buildroot}%{_bindir}/xwalk-pkg-helper +install -m 06755 -p -D src/out/Release/xwalk-pkg-helper %{buildroot}%{_bindir}/xwalk-pkg-helper # Supporting libraries and resources. -install -p -D ${BUILDDIR_NAME}/out/Release/icudtl.dat %{buildroot}%{_libdir}/xwalk/icudtl.dat -install -p -D ${BUILDDIR_NAME}/out/Release/libffmpegsumo.so %{buildroot}%{_libdir}/xwalk/libffmpegsumo.so -install -p -D ${BUILDDIR_NAME}/out/Release/xwalk.pak %{buildroot}%{_libdir}/xwalk/xwalk.pak +install -p -D src/out/Release/icudtl.dat %{buildroot}%{_libdir}/xwalk/icudtl.dat +install -p -D src/out/Release/libffmpegsumo.so %{buildroot}%{_libdir}/xwalk/libffmpegsumo.so +install -p -D src/out/Release/xwalk.pak %{buildroot}%{_libdir}/xwalk/xwalk.pak # Register xwalk to the package manager. -install -p -D ../%{name}.xml %{buildroot}%{_manifestdir}/%{name}.xml -install -p -D ../%{name}.png %{buildroot}%{_desktop_icondir}/%{name}.png - +install -p -D %{name}.xml %{buildroot}%{_manifestdir}/%{name}.xml +install -p -D %{name}.png %{buildroot}%{_desktop_icondir}/%{name}.png %post mkdir -p %{_desktop_icondir_ro} diff --git a/src/PRESUBMIT.py b/src/PRESUBMIT.py index 4dd44ca..16b315e 100644 --- a/src/PRESUBMIT.py +++ b/src/PRESUBMIT.py @@ -240,6 +240,7 @@ _BANNED_CPP_FUNCTIONS = ( _VALID_OS_MACROS = ( # Please keep sorted. 'OS_ANDROID', + 'OS_ANDROID_HOST', 'OS_BSD', 'OS_CAT', # For testing. 'OS_CHROMEOS', diff --git a/src/base/base.gyp b/src/base/base.gyp index a9cc0f4..3025730 100644 --- a/src/base/base.gyp +++ b/src/base/base.gyp @@ -105,19 +105,6 @@ # hence the *_android.cc files are included but the actual code # doesn't have OS_ANDROID / ANDROID defined. 'conditions': [ - # Host build on linux depends on system.gyp::gtk as - # default linux build has TOOLKIT_GTK defined. - ['host_os == "linux"', { - 'sources/': [ - ['include', '^atomicops_internals_x86_gcc\\.cc$'], - ], - 'dependencies': [ - '../build/linux/system.gyp:gtk', - ], - 'export_dependent_settings': [ - '../build/linux/system.gyp:gtk', - ], - }], ['host_os == "mac"', { 'sources/': [ ['exclude', '^native_library_linux\\.cc$'], diff --git a/src/base/base.gypi b/src/base/base.gypi index e130b10..3a442da 100644 --- a/src/base/base.gypi +++ b/src/base/base.gypi @@ -811,8 +811,12 @@ ], }], ['OS == "android" and _toolset == "host" and host_os == "linux"', { + 'defines': [ + 'OS_ANDROID_HOST=Linux', + ], 'sources/': [ # Pull in specific files for host builds. + ['include', '^atomicops_internals_x86_gcc\\.cc$'], ['include', '^threading/platform_thread_linux\\.cc$'], ], }], diff --git a/src/base/message_loop/message_loop.h b/src/base/message_loop/message_loop.h index 956f5f0..742500c 100644 --- a/src/base/message_loop/message_loop.h +++ b/src/base/message_loop/message_loop.h @@ -38,7 +38,7 @@ #include "base/message_loop/message_pump_x11.h" #elif defined(USE_OZONE) && !defined(OS_NACL) #include "base/message_loop/message_pump_ozone.h" -#else +#elif !defined(OS_ANDROID_HOST) #define USE_GTK_MESSAGE_PUMP #include "base/message_loop/message_pump_gtk.h" #if defined(TOOLKIT_GTK) @@ -57,6 +57,8 @@ class RunLoop; class ThreadTaskRunnerHandle; #if defined(OS_ANDROID) class MessagePumpForUI; +#elif defined(OS_ANDROID_HOST) +typedef MessagePumpLibevent MessagePumpForUI; #endif class WaitableEvent; diff --git a/src/build/util/LASTCHANGE b/src/build/util/LASTCHANGE index 65f3efd..6fbdfee 100644 --- a/src/build/util/LASTCHANGE +++ b/src/build/util/LASTCHANGE @@ -1 +1 @@ -LASTCHANGE=254894 +LASTCHANGE=257853 diff --git a/src/media/audio/pulse/pulse.sigs b/src/media/audio/pulse/pulse.sigs index b5d927c..65b2400 100644 --- a/src/media/audio/pulse/pulse.sigs +++ b/src/media/audio/pulse/pulse.sigs @@ -50,3 +50,8 @@ void pa_stream_unref(pa_stream* s); int pa_context_errno(pa_context *c); const char* pa_strerror(int error); pa_cvolume* pa_cvolume_set(pa_cvolume* a, unsigned channels, pa_volume_t v); +# Functions from pulse used in TIZEN to tag the audio stream as "browser". +pa_proplist* pa_proplist_new(void); +void pa_proplist_free(pa_proplist* p); +int pa_proplist_sets(pa_proplist *p, const char *key, const char *value); +pa_stream* pa_stream_new_with_proplist(pa_context* c, const char* name, const pa_sample_spec* ss, const pa_channel_map* map, pa_proplist* proplist); diff --git a/src/media/audio/pulse/pulse_util.cc b/src/media/audio/pulse/pulse_util.cc index bee4790..f29ddad 100644 --- a/src/media/audio/pulse/pulse_util.cc +++ b/src/media/audio/pulse/pulse_util.cc @@ -261,7 +261,16 @@ bool CreateOutputStream(pa_threaded_mainloop** mainloop, // than the default channel map (NULL). map = &source_channel_map; } +#if defined(OS_TIZEN) + pa_proplist* proplist = pa_proplist_new(); + pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, "browser"); + *stream = pa_stream_new_with_proplist(*context, "Playback", + &sample_specifications, + map, proplist); + pa_proplist_free(proplist); +#else *stream = pa_stream_new(*context, "Playback", &sample_specifications, map); +#endif RETURN_ON_FAILURE(*stream, "failed to create PA playback stream"); pa_stream_set_state_callback(*stream, stream_callback, user_data); diff --git a/src/ozone/impl/ozone_display.cc b/src/ozone/impl/ozone_display.cc index 140076f..0b0c7cd 100644 --- a/src/ozone/impl/ozone_display.cc +++ b/src/ozone/impl/ozone_display.cc @@ -18,7 +18,6 @@ namespace ozonewayland { OzoneDisplay* OzoneDisplay::instance_ = NULL; -const int kMaxDisplaySize = 20; OzoneDisplay* OzoneDisplay::GetInstance() { return instance_; @@ -28,8 +27,7 @@ OzoneDisplay::OzoneDisplay() : desktop_screen_(NULL), display_(NULL), channel_(NULL), host_(NULL), - event_converter_(NULL), - spec_(NULL) { + event_converter_(NULL) { instance_ = this; } @@ -38,28 +36,6 @@ OzoneDisplay::~OzoneDisplay() { instance_ = NULL; } -const char* OzoneDisplay::DefaultDisplaySpec() { - if (!spec_) { - spec_ = new char[kMaxDisplaySize]; - if (desktop_screen_ && !desktop_screen_->geometry().size().IsEmpty()) { - gfx::Rect rect(desktop_screen_->geometry()); - base::snprintf(spec_, - kMaxDisplaySize, - "%dx%d*2", - rect.width(), - rect.height()); - } else { - spec_[0] = '\0'; - } - } - - if (spec_[0] == '\0') - NOTREACHED() << - "OutputHandleMode should come from Wayland compositor first"; - - return spec_; -} - bool OzoneDisplay::InitializeHardware() { display_ = new WaylandDisplay(WaylandDisplay::RegisterAsNeeded); bool initialized_hardware = display_->display() ? true : false; @@ -142,8 +118,6 @@ gfx::AcceleratedWidget OzoneDisplay::RealizeAcceleratedWidget( } void OzoneDisplay::OnOutputSizeChanged(unsigned width, unsigned height) { - if (spec_) - base::snprintf(spec_, kMaxDisplaySize, "%dx%d*2", width, height); if (desktop_screen_) desktop_screen_->SetGeometry(gfx::Rect(0, 0, width, height)); } @@ -165,9 +139,6 @@ void OzoneDisplay::Terminate() { if (!event_converter_ && !desktop_screen_) return; - if (spec_) - delete[] spec_; - delete channel_; if (desktop_screen_) { delete desktop_screen_; diff --git a/src/ozone/impl/ozone_display.h b/src/ozone/impl/ozone_display.h index eca8fb9..85855e4 100644 --- a/src/ozone/impl/ozone_display.h +++ b/src/ozone/impl/ozone_display.h @@ -32,7 +32,6 @@ class OZONE_WAYLAND_EXPORT OzoneDisplay : public OutputChangeObserver { OzoneDisplay(); virtual ~OzoneDisplay(); - const char* DefaultDisplaySpec(); bool InitializeHardware(); void ShutdownHardware(); intptr_t GetNativeDisplay(); @@ -58,7 +57,6 @@ class OZONE_WAYLAND_EXPORT OzoneDisplay : public OutputChangeObserver { OzoneDisplayChannel* channel_; OzoneDisplayChannelHost* host_; EventConverterOzoneWayland* event_converter_; - char* spec_; static OzoneDisplay* instance_; DISALLOW_COPY_AND_ASSIGN(OzoneDisplay); }; diff --git a/src/ozone/impl/surface_factory_wayland.cc b/src/ozone/impl/surface_factory_wayland.cc index f58cc83..8496160 100644 --- a/src/ozone/impl/surface_factory_wayland.cc +++ b/src/ozone/impl/surface_factory_wayland.cc @@ -21,10 +21,6 @@ SurfaceFactoryWayland::SurfaceFactoryWayland() : initialized_(false), SurfaceFactoryWayland::~SurfaceFactoryWayland() { } -const char* SurfaceFactoryWayland::DefaultDisplaySpec() { - return OzoneDisplay::GetInstance()->DefaultDisplaySpec(); -} - gfx::Screen* SurfaceFactoryWayland::CreateDesktopScreen() { return OzoneDisplay::GetInstance()->CreateDesktopScreen(); } diff --git a/src/ozone/impl/surface_factory_wayland.h b/src/ozone/impl/surface_factory_wayland.h index aefaec6..37117f6 100644 --- a/src/ozone/impl/surface_factory_wayland.h +++ b/src/ozone/impl/surface_factory_wayland.h @@ -16,7 +16,6 @@ class SurfaceFactoryWayland : public gfx::SurfaceFactoryOzone { SurfaceFactoryWayland(); virtual ~SurfaceFactoryWayland(); - virtual const char* DefaultDisplaySpec() OVERRIDE; virtual gfx::Screen* CreateDesktopScreen() OVERRIDE; virtual SurfaceFactoryOzone::HardwareState InitializeHardware() OVERRIDE; virtual intptr_t GetNativeDisplay() OVERRIDE; diff --git a/src/ozone/impl/window_tree_host_delegate_wayland.cc b/src/ozone/impl/window_tree_host_delegate_wayland.cc index a72903d..9721f17 100644 --- a/src/ozone/impl/window_tree_host_delegate_wayland.cc +++ b/src/ozone/impl/window_tree_host_delegate_wayland.cc @@ -58,8 +58,9 @@ void WindowTreeHostDelegateWayland::OnRootWindowClosed(unsigned handle) { aura_windows_ = NULL; } - if (!current_active_window_ || current_active_window_->window_ != handle || - !open_windows_) { + if (!current_active_window_ || + GetWindowHandle(current_active_window_->window_) != handle || + !open_windows_) { return; } @@ -67,7 +68,7 @@ void WindowTreeHostDelegateWayland::OnRootWindowClosed(unsigned handle) { // Set first top level window in the list of open windows as dispatcher. // This is just a guess of the window which would eventually be focussed. // We should set the correct root window as dispatcher in OnWindowFocused. - const std::list& windows = open_windows(); + const std::list& windows = open_windows(); DesktopWindowTreeHostWayland* rootWindow = DesktopWindowTreeHostWayland::GetHostForAcceleratedWidget( windows.front()); @@ -84,7 +85,7 @@ void WindowTreeHostDelegateWayland::SetActiveWindow( // Make sure the stacking order is correct. The activated window should be // first one in list of open windows. - std::list& windows = open_windows(); + std::list& windows = open_windows(); DCHECK(windows.size()); unsigned window_handle = current_active_window_->window_; if (windows.front() != window_handle) { @@ -120,7 +121,7 @@ WindowTreeHostDelegateWayland::GetCurrentCapture() const { const std::vector& WindowTreeHostDelegateWayland::GetAllOpenWindows() { if (!aura_windows_) { - const std::list& windows = open_windows(); + const std::list& windows = open_windows(); DCHECK(windows.size()); aura_windows_ = new std::vector(windows.size()); std::transform( @@ -147,14 +148,19 @@ void WindowTreeHostDelegateWayland::DispatchMouseEvent( event->StopPropagation(); } -std::list& +std::list& WindowTreeHostDelegateWayland::open_windows() { if (!open_windows_) - open_windows_ = new std::list(); + open_windows_ = new std::list(); return *open_windows_; } +unsigned +WindowTreeHostDelegateWayland::GetWindowHandle(gfx::AcceleratedWidget widget) { + return static_cast(widget); +} + ui::EventProcessor* WindowTreeHostDelegateWayland::GetEventProcessor() { return current_dispatcher_->delegate_->GetEventProcessor(); } @@ -223,8 +229,8 @@ void WindowTreeHostDelegateWayland::OnWindowFocused(unsigned handle) { // Don't dispatch events in case a window has installed itself as capture // window but doesn't have the focus. handle_event_ = current_capture_ ? current_focus_window_ == - current_capture_->GetAcceleratedWidget() : true; - if (current_active_window_->window_ == handle) + GetWindowHandle(current_capture_->GetAcceleratedWidget()) : true; + if (GetWindowHandle(current_active_window_->window_) == handle) return; // A new window should not steal focus in case the current window has a open @@ -257,7 +263,7 @@ void WindowTreeHostDelegateWayland::OnWindowClose(unsigned handle) { // current_capture_ always be a valid pointer. if (!handle || !current_capture_) return; - if (current_capture_->window_ != handle) + if (GetWindowHandle(current_capture_->window_) != handle) return; DesktopWindowTreeHostWayland* window = NULL; window = DesktopWindowTreeHostWayland::GetHostForAcceleratedWidget(handle); diff --git a/src/ozone/impl/window_tree_host_delegate_wayland.h b/src/ozone/impl/window_tree_host_delegate_wayland.h index 51dde45..cc3c2cb 100644 --- a/src/ozone/impl/window_tree_host_delegate_wayland.h +++ b/src/ozone/impl/window_tree_host_delegate_wayland.h @@ -56,7 +56,8 @@ class WindowTreeHostDelegateWayland // Dispatches a mouse event. void DispatchMouseEvent(ui::MouseEvent* event); - std::list& open_windows(); + std::list& open_windows(); + unsigned GetWindowHandle(gfx::AcceleratedWidget widget); unsigned current_focus_window_; bool handle_event_ :1; @@ -70,7 +71,7 @@ class WindowTreeHostDelegateWayland DesktopWindowTreeHostWayland* current_capture_; DesktopWindowTreeHostWayland* current_active_window_; // List of all open windows. - std::list* open_windows_; + std::list* open_windows_; // List of all open aura::Window. std::vector* aura_windows_; DISALLOW_COPY_AND_ASSIGN(WindowTreeHostDelegateWayland); diff --git a/src/tools/android/forwarder2/forwarder.gyp b/src/tools/android/forwarder2/forwarder.gyp index a3f2898..03496a6 100644 --- a/src/tools/android/forwarder2/forwarder.gyp +++ b/src/tools/android/forwarder2/forwarder.gyp @@ -77,10 +77,6 @@ 'host_forwarder_main.cc', 'pipe_notifier.cc', 'socket.cc', - # TODO(pliard): Remove this. This is needed to avoid undefined - # references at link time. - '../../../base/message_loop/message_pump_glib.cc', - '../../../base/message_loop/message_pump_gtk.cc', ], }, ], diff --git a/src/ui/views/views.gyp b/src/ui/views/views.gyp index 01de5c6..39680a0 100644 --- a/src/ui/views/views.gyp +++ b/src/ui/views/views.gyp @@ -4,6 +4,7 @@ { 'variables': { 'chromium_code': 1, + 'external_ozone_views_files': [], }, 'target_defaults': { 'conditions': [ @@ -601,6 +602,9 @@ ], }], ['use_ozone==1', { + 'sources': [ + '<@(external_ozone_views_files)', + ], 'dependencies': [ '../ozone/ozone.gyp:ozone', ], diff --git a/src/v8/build/features.gypi b/src/v8/build/features.gypi index f0e7212..af4b41b 100644 --- a/src/v8/build/features.gypi +++ b/src/v8/build/features.gypi @@ -43,6 +43,9 @@ 'v8_use_snapshot%': 'true', + # Enable XDK profiling support by default. + 'v8_enable_xdkprof': 1, + # With post mortem support enabled, metadata is embedded into libv8 that # describes various parameters of the VM for use by debuggers. See # tools/gen-postmortem-metadata.py for details. diff --git a/src/v8/src/bootstrapper.cc b/src/v8/src/bootstrapper.cc index 6744cb0..b31ad51 100644 --- a/src/v8/src/bootstrapper.cc +++ b/src/v8/src/bootstrapper.cc @@ -1101,7 +1101,7 @@ void Genesis::InitializeGlobal(Handle inner_global, EXTERNAL_##TYPE##_ELEMENTS); \ native_context()->set_##type##_array_fun(*fun); \ } - TYPED_ARRAYS(INSTALL_TYPED_ARRAY) + BUILTIN_TYPED_ARRAY(INSTALL_TYPED_ARRAY) #undef INSTALL_TYPED_ARRAY Handle data_view_fun = diff --git a/src/v8/src/log.cc b/src/v8/src/log.cc index 1c332d1..9eac880 100644 --- a/src/v8/src/log.cc +++ b/src/v8/src/log.cc @@ -43,6 +43,9 @@ #include "string-stream.h" #include "vm-state-inl.h" +// XDK support +#include "third_party/xdk/xdk-v8.h" + namespace v8 { namespace internal { @@ -160,6 +163,15 @@ class CodeEventLogger::NameBuffer { } } + // XDK needs this function temporarily. It will be removed later. + void AppendAddress(Address address) { + Vector buffer(utf8_buffer_ + utf8_pos_, kUtf8BufferSize - utf8_pos_); + int size = OS::SNPrintF(buffer, "0x%x", address); + if (size > 0 && utf8_pos_ + size <= kUtf8BufferSize) { + utf8_pos_ += size; + } + } + const char* get() { return utf8_buffer_; } int size() const { return utf8_pos_; } @@ -656,6 +668,226 @@ class JitLogger : public CodeEventLogger { void* StartCodePosInfoEvent(); void EndCodePosInfoEvent(Code* code, void* jit_handler_data); + + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + // XDK needs all this stuff below to generate the strings like + // "code-creation:..." in the same format as Logger generates. + // This string is sent to XDK by code_event_handler and then used for + // postprocessing. All these CodeCreateEvent(...) functions and helpers + // will be removed before commit. + void AppendCodeCreateHeader(NameBuffer* msg, + Logger::LogEventsAndTags tag, + Code* code) { + CHECK(msg); + msg->AppendBytes(kLogEventsNames[Logger::CODE_CREATION_EVENT]); + msg->AppendByte(','); + msg->AppendBytes(kLogEventsNames[tag]); + msg->AppendByte(','); + msg->AppendInt(code->kind()); + msg->AppendByte(','); + msg->AppendAddress(code->instruction_start()); + msg->AppendByte(','); + msg->AppendInt(code->instruction_size()); + msg->AppendByte(','); + } + + + void AppendDetailed(NameBuffer* msg, String* str, bool show_impl_info) { + CHECK(msg); + if (str == NULL) return; + DisallowHeapAllocation no_gc; // Ensure string stay valid. + int len = str->length(); + if (len > 0x1000) + len = 0x1000; + if (show_impl_info) { + msg->AppendByte(str->IsOneByteRepresentation() ? 'a' : '2'); + if (StringShape(str).IsExternal()) + msg->AppendByte('e'); + if (StringShape(str).IsInternalized()) + msg->AppendByte('#'); + msg->AppendByte(':'); + msg->AppendInt(str->length()); + msg->AppendByte(':'); + } + for (int i = 0; i < len; i++) { + uc32 c = str->Get(i); + if (c > 0xff) { + msg->AppendBytes("\\u"); + msg->AppendHex(c); + } else if (c < 32 || c > 126) { + msg->AppendBytes("\\x"); + msg->AppendHex(c); + } else if (c == ',') { + msg->AppendBytes("\\,"); + } else if (c == '\\') { + msg->AppendBytes("\\\\"); + } else if (c == '\"') { + msg->AppendBytes("\"\""); + } else { + msg->AppendByte(c); + } + } + } + + + void AppendDoubleQuotedString(NameBuffer* msg, const char* string) { + CHECK(msg); + msg->AppendByte('"'); + for (const char* p = string; *p != '\0'; p++) { + if (*p == '"') { + msg->AppendByte('\\'); + } + msg->AppendByte(*p); + } + msg->AppendByte('"'); + } + + + void AppendSymbolName(NameBuffer* msg, Symbol* symbol) { + CHECK(msg); + ASSERT(symbol); + msg->AppendBytes("symbol("); + if (!symbol->name()->IsUndefined()) { + msg->AppendByte('"'); + AppendDetailed(msg, String::cast(symbol->name()), false); + msg->AppendBytes("\" "); + } + msg->AppendBytes("hash "); + msg->AppendHex(symbol->Hash()); + msg->AppendByte(')'); + } + + + virtual void CodeCreateEvent(Logger::LogEventsAndTags tag, + Code* code, const char* comment) { + if (!xdk::XDKIsAgentAlive()) { + CodeEventLogger::CodeCreateEvent(tag, code, comment); + return; + } + + name_buffer_->Reset(); + AppendCodeCreateHeader(name_buffer_, tag, code); + AppendDoubleQuotedString(name_buffer_, comment); + LogRecordedBuffer(code, NULL, name_buffer_->get(), name_buffer_->size()); + } + + + virtual void CodeCreateEvent(Logger::LogEventsAndTags tag, + Code* code, Name* name) { + if (!xdk::XDKIsAgentAlive()) { + CodeEventLogger::CodeCreateEvent(tag, code, name); + return; + } + + name_buffer_->Reset(); + AppendCodeCreateHeader(name_buffer_, tag, code); + if (name->IsString()) { + name_buffer_->AppendByte('"'); + AppendDetailed(name_buffer_, String::cast(name), false); + name_buffer_->AppendByte('"'); + } else { + AppendSymbolName(name_buffer_, Symbol::cast(name)); + } + LogRecordedBuffer(code, NULL, name_buffer_->get(), name_buffer_->size()); + } + + + virtual void CodeCreateEvent(Logger::LogEventsAndTags tag, + Code* code, + SharedFunctionInfo* shared, + CompilationInfo* info, + Name* name) { + if (!xdk::XDKIsAgentAlive()) { + CodeEventLogger::CodeCreateEvent(tag, code, shared, info, name); + return; + } + + name_buffer_->Reset(); + AppendCodeCreateHeader(name_buffer_, tag, code); + if (name->IsString()) { + SmartArrayPointer str = + String::cast(name)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); + name_buffer_->AppendByte('"'); + name_buffer_->AppendBytes(str.get()); + name_buffer_->AppendByte('"'); + } else { + AppendSymbolName(name_buffer_, Symbol::cast(name)); + } + name_buffer_->AppendByte(','); + name_buffer_->AppendAddress(shared->address()); + name_buffer_->AppendByte(','); + name_buffer_->AppendBytes(ComputeMarker(code)); + LogRecordedBuffer(code, shared, name_buffer_->get(), name_buffer_->size()); + } + + virtual void CodeCreateEvent(Logger::LogEventsAndTags tag, + Code* code, + SharedFunctionInfo* shared, + CompilationInfo* info, + Name* source, + int line, int column) { + if (!xdk::XDKIsAgentAlive()) { + CodeEventLogger::CodeCreateEvent(tag, code, shared, + info, source, line, column); + return; + } + + name_buffer_->Reset(); + AppendCodeCreateHeader(name_buffer_, tag, code); + SmartArrayPointer name = + shared->DebugName()->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); + name_buffer_->AppendByte('"'); + name_buffer_->AppendBytes(name.get()); + name_buffer_->AppendByte(' '); + if (source->IsString()) { + SmartArrayPointer sourcestr = + String::cast(source)->ToCString(DISALLOW_NULLS, + ROBUST_STRING_TRAVERSAL); + name_buffer_->AppendBytes(sourcestr.get()); + } else { + AppendSymbolName(name_buffer_, Symbol::cast(source)); + } + name_buffer_->AppendByte(':'); + name_buffer_->AppendInt(line); + name_buffer_->AppendByte(':'); + name_buffer_->AppendInt(column); + name_buffer_->AppendBytes("\","); + name_buffer_->AppendAddress(shared->address()); + name_buffer_->AppendByte(','); + name_buffer_->AppendBytes(ComputeMarker(code)); + LogRecordedBuffer(code, shared, name_buffer_->get(), name_buffer_->size()); + } + + + virtual void CodeCreateEvent(Logger::LogEventsAndTags tag, + Code* code, + int args_count) { + if (!xdk::XDKIsAgentAlive()) { + CodeEventLogger::CodeCreateEvent(tag, code, args_count); + return; + } + + name_buffer_->Reset(); + AppendCodeCreateHeader(name_buffer_, tag, code); + name_buffer_->AppendBytes("\"args_count: "); + name_buffer_->AppendInt(args_count); + name_buffer_->AppendByte('"'); + } + + + virtual void RegExpCodeCreateEvent(Code* code, String* source) { + if (!xdk::XDKIsAgentAlive()) { + CodeEventLogger::RegExpCodeCreateEvent(code, source); + return; + } + + name_buffer_->Reset(); + AppendCodeCreateHeader(name_buffer_, Logger::REG_EXP_TAG, code); + name_buffer_->AppendByte('"'); + AppendDetailed(name_buffer_, source, false); + name_buffer_->AppendByte('"'); + } + private: virtual void LogRecordedBuffer(Code* code, SharedFunctionInfo* shared, @@ -1376,8 +1608,13 @@ static void AppendCodeCreateHeader(Log::MessageBuilder* msg, kLogEventsNames[Logger::CODE_CREATION_EVENT], kLogEventsNames[tag], code->kind()); - msg->AppendAddress(code->address()); - msg->Append(",%d,", code->ExecutableSize()); + if (xdk::XDKIsAgentAlive()) { + msg->AppendAddress(code->instruction_start()); + msg->Append(",%d,", code->instruction_size()); + } else { + msg->AppendAddress(code->address()); + msg->Append(",%d,", code->ExecutableSize()); + } } @@ -1623,11 +1860,30 @@ void Logger::MoveEventInternal(LogEventsAndTags event, Address from, Address to) { if (!FLAG_log_code || !log_->IsEnabled()) return; + + Code* from_code = NULL; + Address to_code = NULL; + if (xdk::XDKIsAgentAlive()) { + from_code = Code::cast(HeapObject::FromAddress(from)); + const size_t header_size = + from_code->instruction_start() - reinterpret_cast(from_code); + to_code = + reinterpret_cast(HeapObject::FromAddress(to)) + header_size; + } + Log::MessageBuilder msg(log_); msg.Append("%s,", kLogEventsNames[event]); - msg.AppendAddress(from); + if (xdk::XDKIsAgentAlive()) { + msg.AppendAddress(from_code->instruction_start()); + } else { + msg.AppendAddress(from); + } msg.Append(','); - msg.AppendAddress(to); + if (xdk::XDKIsAgentAlive()) { + msg.AppendAddress(to_code); + } else { + msg.AppendAddress(to); + } msg.Append('\n'); msg.WriteToLogFile(); } @@ -1748,6 +2004,15 @@ void Logger::TickEvent(TickSample* sample, bool overflow) { } +void Logger::XDKResumeProfiler() { + if (!log_->IsEnabled()) return; + if (profiler_ != NULL) { + profiler_->resume(); + is_logging_ = true; + } +} + + void Logger::StopProfiler() { if (!log_->IsEnabled()) return; if (profiler_ != NULL) { @@ -1886,6 +2151,12 @@ void Logger::LogCodeObject(Object* object) { void Logger::LogCodeObjects() { + // Starting from Chromium v34 this function is also called from + // V8::Initialize. This causes reading the heap to collect already + // compiled methods. For XDK that must be done because XDK profiler + // consumes CODE_ADDED events and mantains a map of compiled methods. + if (xdk::XDKIsAgentAlive()) return; + Heap* heap = isolate_->heap(); heap->CollectAllGarbage(Heap::kMakeHeapIterableMask, "Logger::LogCodeObjects"); @@ -1946,6 +2217,12 @@ void Logger::LogExistingFunction(Handle shared, void Logger::LogCompiledFunctions() { + // Starting from Chromium v34 this function is also called from + // V8::Initialize. This causes reading the heap to collect already + // compiled methods. For XDK that must be done because XDK profiler + // consumes CODE_ADDED events and mantains a map of compiled methods. + if (xdk::XDKIsAgentAlive()) return; + Heap* heap = isolate_->heap(); heap->CollectAllGarbage(Heap::kMakeHeapIterableMask, "Logger::LogCompiledFunctions"); @@ -2081,10 +2358,19 @@ bool Logger::SetUp(Isolate* isolate) { is_logging_ = true; } + xdk::XDKInitializeForV8(isolate); + if (FLAG_prof) { profiler_ = new Profiler(isolate); is_logging_ = true; profiler_->Engage(); + + if (xdk::XDKIsAgentAlive()) { + // A way to to start profiler in pause mode was removed. + // To pause collection of the CPU ticks we need to emulate pause. + // This will be removed later once XDK agent will have own sampler. + profiler_->pause(); + } } if (FLAG_log_internal_timer_events || FLAG_prof) timer_.Start(); diff --git a/src/v8/src/log.h b/src/v8/src/log.h index d4dc76a..041ede0 100644 --- a/src/v8/src/log.h +++ b/src/v8/src/log.h @@ -360,6 +360,13 @@ class Logger { // When data collection is paused, CPU Tick events are discarded. void StopProfiler(); + // Resumes collection of CPU Tick events. + void XDKResumeProfiler(); + + // XDK agent uses it log code map (list of CodeAdded event + // before resume CPU Tick events. + Log* XDKGetLog() { return log_; } + void LogExistingFunction(Handle shared, Handle code); // Logs all compiled functions found in the heap. @@ -528,15 +535,15 @@ class CodeEventLogger : public CodeEventListener { virtual void SharedFunctionInfoMoveEvent(Address from, Address to) { } virtual void CodeMovingGCEvent() { } - private: + protected: class NameBuffer; + NameBuffer* name_buffer_; + private: virtual void LogRecordedBuffer(Code* code, SharedFunctionInfo* shared, const char* name, int length) = 0; - - NameBuffer* name_buffer_; }; diff --git a/src/v8/src/objects.h b/src/v8/src/objects.h index a41e10a..4f65328 100644 --- a/src/v8/src/objects.h +++ b/src/v8/src/objects.h @@ -4670,7 +4670,7 @@ class FreeSpace: public HeapObject { // V has parameters (Type, type, TYPE, C type, element_size) -#define TYPED_ARRAYS(V) \ +#define BUILTIN_TYPED_ARRAY(V) \ V(Uint8, uint8, UINT8, uint8_t, 1) \ V(Int8, int8, INT8, int8_t, 1) \ V(Uint16, uint16, UINT16, uint16_t, 2) \ @@ -4679,11 +4679,18 @@ class FreeSpace: public HeapObject { V(Int32, int32, INT32, int32_t, 4) \ V(Float32, float32, FLOAT32, float, 4) \ V(Float64, float64, FLOAT64, double, 8) \ - V(Float32x4, float32x4, FLOAT32x4, v8::internal::float32x4_value_t, 16) \ - V(Int32x4, int32x4, INT32x4, v8::internal::int32x4_value_t, 16) \ V(Uint8Clamped, uint8_clamped, UINT8_CLAMPED, uint8_t, 1) +#define SIMD128_TYPED_ARRAY(V) \ + V(Float32x4, float32x4, FLOAT32x4, v8::internal::float32x4_value_t, 16) \ + V(Int32x4, int32x4, INT32x4, v8::internal::int32x4_value_t, 16) + + +#define TYPED_ARRAYS(V) \ + BUILTIN_TYPED_ARRAY(V) \ + SIMD128_TYPED_ARRAY(V) + // An ExternalArray represents a fixed-size array of primitive values // which live outside the JavaScript heap. Its subclasses are used to diff --git a/src/v8/src/third_party/xdk/xdk-agent.cc b/src/v8/src/third_party/xdk/xdk-agent.cc new file mode 100644 index 0000000..96bf8ee --- /dev/null +++ b/src/v8/src/third_party/xdk/xdk-agent.cc @@ -0,0 +1,451 @@ +// Copyright (c) 2013 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifdef __linux__ +#include +#endif // __linux__ + +#include "xdk-agent.h" +#include +#include +#include +#include "platform.h" +#include "log-utils.h" + +namespace xdk { +namespace internal { + +static unsigned int XDK_COMMAND_LENGTH = 100; // It should be enough. + +static const char* XDK_TRACE_FILE = + "/data/data/com.intel.app_analyzer/files/result.xdk2v8"; + +static const char* XDK_MARKER_FILE = + "/data/data/com.intel.app_analyzer/files/profiler.run"; + +XDKAgent XDKAgent::instance_; + +// SetIdle has the same semantics as CpuProfiler::SetIdle has (v8/src/api.cc) +// It is used to tell the sampler that XDK agent is idle (it is not busy with +// some tasks). If the agent is idle that the sampler put a IDLE VM state into +// the Tick record. The samples happen during IDLE will be attributed to (idle) +// line in the XDK viewer. +static void SetIdle(bool isIdle, v8engine::Isolate* isolate) { + CHECK(isolate); + v8engine::StateTag state = isolate->current_vm_state(); + if (isolate->js_entry_sp() != NULL) return; + if (state == v8engine::EXTERNAL || state == v8engine::IDLE) { + if (isIdle) { + isolate->set_current_vm_state(v8engine::IDLE); + } else if (state == v8engine::IDLE) { + isolate->set_current_vm_state(v8engine::EXTERNAL); + } + } +} + + +bool XDKAgent::setUp(v8engine::Isolate* isolate) { + CHECK(isolate); + + if (m_isolate) { + // The setUp method is called for the main thread first, then may be called + // again if the app uses Workers (each Worker object has own V8 instance). + // XDK agent does not support JavaScript Worker currently. + XDKLog("xdk: Agent is already initialized\n"); + return false; + } + + FILE* file = v8engine::OS::FOpen(XDK_MARKER_FILE, "r"); + if (file == NULL) { + return false; + } + + fclose(file); + m_alive = true; + m_isolate = isolate; + + return m_alive; +} + + +void XDKAgent::resumeSampling() { + v8engine::LockGuard l(m_agent_access); + CHECK(m_isolate); + + v8engine::Log* log = m_isolate->logger()->XDKGetLog(); + CHECK(log); + + // Create a new log file for new profiling session + CHECK(!log->IsEnabled()); + log->Initialize(XDK_TRACE_FILE); + +#ifdef __linux__ + int mode = S_IRUSR|S_IROTH|S_IRGRP|S_IWUSR|S_IWOTH|S_IWGRP; + if (chmod(XDK_TRACE_FILE, mode) != 0) { + XDKLog("xdk: Couldn't change permissions for a trace file\n"); + } +#endif // __linux__ + + CHECK(log->IsEnabled()); + + logFunctionSnapshot(); + + // Write a marker line into the log for testing purpose + v8engine::Log::MessageBuilder msg(log); + msg.Append("Profiler started.\n"); + msg.WriteToLogFile(); + + // Resume collection the CPU Tick events + m_isolate->logger()->XDKResumeProfiler(); + XDKLog("xdk: Sampling is resumed\n"); + + SetIdle(true, m_isolate); +} + + +void XDKAgent::pauseSampling() { + // Pause collection the CPU Tick events + CHECK(m_isolate); + m_isolate->logger()->StopProfiler(); + + // Use v8 logger internals to close the trace file. + // Once XDK agent implements own sampler this will be removed. + v8engine::Log* log = m_isolate->logger()->XDKGetLog(); + CHECK(log); + log->stop(); + log->Close(); + + XDKLog("xdk: Sampling is stopped\n"); +} + + +struct ObjectDeallocator { + template + void operator()(const T& obj) const { delete obj.second; } +}; + + +XDKAgent::~XDKAgent() { + CHECK(m_server != NULL); + CHECK(m_agent_access != NULL); + + if (m_alive) { + CHECK(m_isolate != NULL); + + m_terminate = true; + + std::for_each(m_lineMaps.begin(), m_lineMaps.end(), ObjectDeallocator()); + m_lineMaps.clear(); + + m_server->Shutdown(); + + Join(); + } + + delete m_server; + m_server = NULL; + + delete m_agent_access; + m_agent_access = NULL; + + m_isolate = NULL; +} + + +// The XDK listener thread. +void XDKAgent::Run() { + v8engine::Isolate::EnsureDefaultIsolate(); + v8engine::DisallowHeapAllocation no_allocation; + v8engine::DisallowHandleAllocation no_handles; + v8engine::DisallowHandleDereference no_deref; + + XDKLog("xdk: Listener thread is running\n"); + CHECK(m_server); + + bool ok = m_server->Bind(m_port); + if (!ok) { + XDKLog("xdk: Unable to bind port=%d %d\n", + m_port, v8engine::Socket::GetLastError()); + return; + } + + std::vector buf(XDK_COMMAND_LENGTH); + + const std::string cmdStart = "start"; + const std::string cmdStop = "stop"; + + while (!m_terminate) { + XDKLog("xdk: Listener thread is waiting for connection\n"); + + ok = m_server->Listen(1); + XDKLog("xdk: Listener thread got a connection request. Return value=%d\n", + v8engine::Socket::GetLastError()); + if (ok) { + v8engine::Socket* client = m_server->Accept(); + if (client == NULL) { + XDKLog("xdk: Accept failed %d\n", v8engine::Socket::GetLastError()); + continue; + } + + XDKLog("xdk: Connected\n"); + + int bytes_read = client->Receive(&buf[0], buf.size() - 1); + if (bytes_read == 0) { + XDKLog("xdk: Receive failed %d\n", v8engine::Socket::GetLastError()); + break; + } + buf[bytes_read] = '\0'; + + #ifdef WIN32 + if (bytes_read > 3) buf[bytes_read - 2] = '\0'; // remove CR+LF symbols + #else + if (bytes_read > 2) buf[bytes_read - 1] = '\0'; // remove LF symbol + #endif + + std::string clientCommand(&buf[0]); + XDKLog("xdk: Got '%s' profiling command\n", clientCommand.c_str()); + + if (clientCommand == cmdStart) { + resumeSampling(); + } else if (clientCommand == cmdStop) { + pauseSampling(); + } else { + XDKLog("xdk: '%s' is not handled command\n", clientCommand.c_str()); + break; + } + } + } + + XDKLog("xdk: Listener thread is stopped\n"); + return; +} + + +void XDKAgent::processCodeMovedEvent(const v8::JitCodeEvent* event) { + v8engine::LockGuard l(m_agent_access); + v8engine::Address from = static_cast(event->code_start); + v8engine::Address to = static_cast(event->new_code_start); + + if (!from || !to) return; + XDKLog("xdk: CODE_MOVED from=0x%x to=0x%x\n", from, to); + m_snapshot.move(from, to); +} + + +void XDKAgent::processCodeRemovedEvent(const v8::JitCodeEvent* event) { + v8engine::LockGuard l(m_agent_access); + v8engine::Address addr = static_cast(event->code_start); + + if (!addr) return; + XDKLog("xdk: CODE_REMOVED for addr=0x%x\n", addr); + m_snapshot.remove(addr); +} + + +void XDKAgent::processCodeAddedEvent(const v8::JitCodeEvent* event) { + v8engine::LockGuard l(m_agent_access); + + v8engine::Address codeAddr = + static_cast(event->code_start); + uint32_t codeLen = event->code_len; + + if (!codeAddr || !codeLen) return; + XDKLog("xdk: CODE_ADDED for addr=0x%x len=0x%x\n", codeAddr, codeLen); + + // Look for line number information + LineMap* lineMap = NULL; + LineMaps::iterator itr = m_lineMaps.find(codeAddr); + if (itr == m_lineMaps.end()) { + XDKLog("xdk: Unable to find line info for addr=0x%x\n", codeAddr); + } else { + lineMap = itr->second; + + // Remove line map if no chance to get source lines for it + v8::Handle script = event->script; + if (*script == NULL) { + XDKLog("xdk: Script is empty. No line info for addr=0x%x.\n", codeAddr); + delete lineMap; + lineMap = NULL; + m_lineMaps.erase(codeAddr); + } else { + // Convert V8 pos value into source line number. + LineMap::Entries* entries = + const_cast(lineMap->getEntries()); + CHECK(entries); + CHECK(entries->size()); + XDKLog("xdk: Found line info (%d lines) for addr=0x%x\n", + entries->size(), codeAddr); + size_t srcLine = 0; + LineMap::Entries::iterator lineItr = entries->begin(); + LineMap::Entries::iterator lineEnd = entries->end(); + for (; lineItr != lineEnd; ++lineItr) { + srcLine = script->GetLineNumber(lineItr->line) + 1; + lineItr->line = srcLine; + XDKLog("xdk: offset=%p line=%d\n", lineItr->pcOffset, lineItr->line); + } + } + } + + std::string funcType; + std::string name(event->name.str, event->name.len); + Function func(codeAddr, codeLen, name, funcType, lineMap); + + if (lineMap) { + // Put the line number information for the given method into the trace file + // if profiling session is running. + logLineNumberInfo(codeAddr, *lineMap); + + // Release memory allocated on CODE_START_LINE_INFO_RECORDING + delete lineMap; + lineMap = NULL; + m_lineMaps.erase(codeAddr); + } + + m_snapshot.insert(func); +} + + +void XDKAgent::processLineMapAddedEvent(const v8::JitCodeEvent* event) { + v8engine::LockGuard l(m_agent_access); + v8engine::Address codeAddr = + static_cast(event->code_start); + void* userData = event->user_data; + + if (!userData || !codeAddr) return; + + LineMap* lineMap = reinterpret_cast(userData); + if (lineMap->getSize() == 0) { + XDKLog("xdk: CODE_END_LINE no entries for user_data=%p addr=0x%x\n", + userData, codeAddr); + return; + } + + std::pair + result = m_lineMaps.insert(std::make_pair(codeAddr, lineMap)); + if (!result.second) { + m_lineMaps.erase(codeAddr); + XDKLog("xdk: removed unprocessed line info for addr=0x%x\n", codeAddr); + result = m_lineMaps.insert(std::make_pair(codeAddr, lineMap)); + CHECK(result.second); + } + + XDKLog("xdk: CODE_END_LINE added %d entries for user_data=%p addr=0x%x\n", + lineMap->getSize(), userData, codeAddr); +} + + +void EventHandler(const v8::JitCodeEvent* event) { + // This callback is called regardless of whether profiling is running. + // + // By default profiling is launched in paused mode, the agent is awaiting + // a command to resume profiling. At the same time, V8's JIT compiler is + // working. The functions which are JIT-compiled while sampling is paused + // are cached by V8's Logger and will be written in log (trace file) when + // XDK resumes the profiling. The line number info for such functions are not + // cached. We need to capture and cache the line number info and flush + // the cache on resume profiling. + + if (event == NULL) return; + + switch (event->type) { + case v8::JitCodeEvent::CODE_MOVED: + XDKAgent::instance().processCodeMovedEvent(event); + break; + + case v8::JitCodeEvent::CODE_REMOVED: + XDKAgent::instance().processCodeRemovedEvent(event); + break; + + case v8::JitCodeEvent::CODE_ADDED: + XDKAgent::instance().processCodeAddedEvent(event); + + case v8::JitCodeEvent::CODE_ADD_LINE_POS_INFO: { + void* userData = event->user_data; + if (!userData) return; + LineMap* lineMap = reinterpret_cast(userData); + size_t offset = event->line_info.offset; + size_t pos = event->line_info.pos; + lineMap->setPosition(offset, pos); + XDKLog("xdk: CODE_ADD_LINE_POS for user_data=%p offset=0x%x pos=%d\n", + userData, offset, pos); + break; + } + + case v8::JitCodeEvent::CODE_START_LINE_INFO_RECORDING: { + v8::JitCodeEvent* data = const_cast(event); + data->user_data = new LineMap(); + XDKLog("xdk: CODE_START_LINE for user_data=%p\n", event->user_data); + break; + } + + case v8::JitCodeEvent::CODE_END_LINE_INFO_RECORDING: { + XDKAgent::instance().processLineMapAddedEvent(event); + break; + } + + default: + XDKLog("xdk: Unknown event\n"); + break; + } + + SetIdle(true, XDKAgent::instance().isolate()); + + return; +} + + +void XDKAgent::logLineNumberInfo(v8engine::Address addr, + const LineMap& lineInfo) { + CHECK(addr); + v8engine::Log* log = m_isolate->logger()->XDKGetLog(); + CHECK(log); + if (!log->IsEnabled()) return; + if (lineInfo.getSize() == 0) return; + const LineMap::Entries* lines = lineInfo.getEntries(); + CHECK(lines); + LineMap::Entries::const_iterator lineItr = lines->begin(); + LineMap::Entries::const_iterator lineEnd = lines->end(); + + // Put 'src-pos' lines into the log in our own format + for (; lineItr != lineEnd; ++lineItr) { + v8engine::Log::MessageBuilder msg(m_isolate->logger()->XDKGetLog()); + msg.Append("src-pos,"); + msg.Append("0x%x,%d,%d\n", addr, lineItr->pcOffset, lineItr->line); + msg.WriteToLogFile(); + } +} + + +void XDKAgent::logFunctionSnapshot() { + CHECK(m_isolate); + + CodeMap::const_iterator funcItr = m_snapshot.entries().begin(); + CodeMap::const_iterator funcEnd = m_snapshot.entries().end(); + + XDKLog("FunctionSnapshot: %d entries\n", m_snapshot.entries().size()); + if (m_snapshot.entries().size() == 0) return; + + unsigned int i = 1; + + for (; funcItr != funcEnd; ++funcItr, i++) { + const Range& range = funcItr->first; + const Function& func = funcItr->second; + + XDKLog("%d %s\n", i, func.getLogLine().c_str()); + + const LineMap& map = func.getLineMap(); + if (map.getSize()) { + v8engine::Address codeAddr = range.start(); + XDKLog(" Found %d lines for addr=%p\n", map.getSize(), codeAddr); + logLineNumberInfo(codeAddr, map); + } + + // Write 'code-creation' line into the log + v8engine::Log::MessageBuilder msg(m_isolate->logger()->XDKGetLog()); + msg.Append("%s\n", func.getLogLine().c_str()); + msg.WriteToLogFile(); + } +} + +}} // namespace xdk::internal diff --git a/src/v8/src/third_party/xdk/xdk-agent.h b/src/v8/src/third_party/xdk/xdk-agent.h new file mode 100644 index 0000000..183f514 --- /dev/null +++ b/src/v8/src/third_party/xdk/xdk-agent.h @@ -0,0 +1,111 @@ +// Copyright (c) 2013 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef XDK_AGENT_H_ +#define XDK_AGENT_H_ + +#include "v8.h" +#include "sampler.h" +#include "platform.h" +#include "platform/socket.h" +#include "platform/mutex.h" +#include "xdk-types.h" +#include "xdk-code-map.h" + +// ---------------------------------------------------------------------------- +// +// This file declares XDKAgent class which does +// +// - Handles the code events to maintain the code map. +// - Handles the line info events to assosiate the line info with code event. +// - Accepts start / stop profiling commands from AppAnalyzer. +// +// ---------------------------------------------------------------------------- + +namespace xdk { +namespace internal { + +const int XDK_AGENT_PORT = 48899; + +// Callback called by V8 builtin logger. +void EventHandler(const v8::JitCodeEvent* event); + +// XDK profiling agent. It starts a socket listener on the specific port and +// handles commands to start and stop sampling. +class XDKAgent : public v8engine::Thread { + public: + static XDKAgent& instance() { + return instance_; + } + + void Run(); + + bool setUp(v8engine::Isolate* isolate); + bool isAlive() const { return m_alive; } + + void processCodeMovedEvent(const v8::JitCodeEvent* event); + void processCodeRemovedEvent(const v8::JitCodeEvent* event); + void processCodeAddedEvent(const v8::JitCodeEvent* event); + void processLineMapAddedEvent(const v8::JitCodeEvent* event); + + inline v8engine::Isolate* isolate() { return m_isolate; } + + private: + virtual ~XDKAgent(); + XDKAgent() + : v8engine::Thread("xdk:agent"), + m_alive(false), + m_port(XDK_AGENT_PORT), + m_server(new v8engine::Socket()), + m_agent_access(new v8engine::Mutex()), + m_isolate(NULL), + m_terminate(false) { + CHECK(m_server != NULL); + CHECK(m_agent_access != NULL); + } + XDKAgent(const XDKAgent&); + XDKAgent& operator=(const XDKAgent&); + + void logFunctionSnapshot(); + void logLineNumberInfo(v8engine::Address codeAddr, const LineMap& lineInfo); + + void resumeSampling(); + void pauseSampling(); + + bool m_alive; + + const int m_port; // Port to use for the agent. + v8engine::Socket* m_server; // Server socket for listen/accept. + v8engine::Mutex* m_agent_access; + v8engine::Isolate* m_isolate; + + // The snapshot of compiled methods at present moment. + FunctionSnapshot m_snapshot; + + // The processLineMapAddedEvent function adds a new map for code starting + // address. Newly added map describes how ps offsets maps to internal pos, + // but not how ps offsets maps to line number within source file. + // + // On CodeAdd event, processCodeAddedEvent function looks for line map for + // a code address. If map is found that assign it to a object of Function type + // in FunctionSnapshot. Before assign the pc offset to pos map is converted + // to pc offset to source line. + // + // CodeMoved and CodeRemoved must not affect this map. + // Current understanding of V8 code generator: V8 first emits LineStart event, + // then bunch of LineAdd events, then LineEnd event, finally CodeAdded event. + // Based on above no need to add any 'smart' logic on CodeMoved and + // CodeRemoved for line map. + // + // Basically it should be always empty. + LineMaps m_lineMaps; + + bool m_terminate; // Termination flag for listening thread. + + static XDKAgent instance_; +}; + +} } // namespace xdk::internal + +#endif // XDK_AGENT_H_ diff --git a/src/v8/src/third_party/xdk/xdk-code-map.cc b/src/v8/src/third_party/xdk/xdk-code-map.cc new file mode 100644 index 0000000..2f25c4c --- /dev/null +++ b/src/v8/src/third_party/xdk/xdk-code-map.cc @@ -0,0 +1,145 @@ +// Copyright (c) 2013 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "xdk-code-map.h" +#include + +namespace xdk { +namespace internal { + +static std::string replaceAddress(const std::string& str, + v8engine::Address addr) { + // The input str: code-creation,LazyCompile,0,0x3851c4e0,200," native uri.js" + std::string first; + std::string end; + + std::size_t found = str.find(','); + if (found != std::string::npos) { + found = str.find(',', found + 1); + if (found != std::string::npos) { + found = str.find(',', found + 1); + if (found != std::string::npos) { + first = str.substr(0, found); + found = str.find(',', found + 1); + if (found != std::string::npos) { + end = str.substr(found, str.size() - found); + } + } + } + } + + if (!first.size() || !end.size()) return str; + + std::stringstream ss; + ss << first << ',' + << std::showbase << std::hex << static_cast(addr) << end; + return ss.str(); +} + + +Function::Function(v8engine::Address codeAddr, uint32_t codeLen, + const std::string& name, const std::string& type, + const LineMap* lineMap) + : m_codeAddr(codeAddr), m_codeLen(codeLen), m_name(name), m_type(type) { + CHECK(codeAddr); + CHECK(codeLen); + // Can't be empty because it's came from CodeCreation(...) events + CHECK(!name.empty()); + m_logLine = m_name; + + if (lineMap && lineMap->getSize()) m_lineMap = *lineMap; +} + + +void FunctionSnapshot::removeAll(const Range& range) { + CodeMap::iterator low = m_impl.lower_bound(range); + CodeMap::iterator up = m_impl.upper_bound(range); + CodeMap::iterator::difference_type num = std::distance(low, up); + + if (num) { + XDKLog("xdk: %d ranges were overlapped and removed\n", num); + + CodeMap::iterator itr = low; + for (; itr != up; ++itr) { + XDKLog("xdk: ovrl&removed addr=0x%x len=0x%x name=%s\n", + itr->first.start(), itr->first.length(), + itr->second.getLogLine().c_str()); + } + m_impl.erase(low, up); + } +} + + +void FunctionSnapshot::insert(const Function& func) { + v8engine::Address codeAddr = func.getCodeAddress(); + uint32_t codeLen = func.getCodeLength(); + CHECK(codeAddr); + CHECK(codeLen); + + Range range(codeAddr, codeLen); + + removeAll(range); + + std::pair res = + m_impl.insert(std::make_pair(range, func)); + CHECK(res.second); + + XDKLog("xdk: size=%d added addr=0x%x name=%s\n", + m_impl.size(), range.start(), func.getLogLine().c_str()); +} + + +void FunctionSnapshot::remove(v8engine::Address codeAddr) { + if (!codeAddr) return; + CodeMap::iterator itr = m_impl.find(Range(codeAddr, 1)); + if (itr != m_impl.end()) { + std::string name = itr->second.getLogLine(); + uint32_t len = itr->first.length(); + m_impl.erase(itr); + XDKLog("xdk: size=%d removed addr=0x%x name=%s\n", + m_impl.size(), codeAddr, len, name.c_str()); + } +} + + +void FunctionSnapshot::move(v8engine::Address from, v8engine::Address to) { + if (!from || !to) return; + if (from == to) return; + + CodeMap::iterator itr = m_impl.find(Range(from, 1)); + if (itr == m_impl.end()) { + XDKLog("xdk: couldn't find a code to move from=0x%x to=0x%x\n", from, to); + return; + } + if (itr->first.start() != from) { + XDKLog("xdk: discarded move from=0x%x to=0x%x\n", from, to); + return; + } + + uint32_t codeLen = itr->second.getCodeLength(); + const LineMap& lines = itr->second.getLineMap(); + + // In case of CodeMoved we have to check that name contains the same code + // addr and code length as the input params and replace if they are different. + const std::string& orig = itr->second.getName(); + std::string name = replaceAddress(orig, to); + + const std::string& type = itr->second.getType(); + Function toEntry(to, codeLen, name, type, &lines); + + m_impl.erase(itr); + + Range range(to, codeLen); + removeAll(range); + + // Now ready to move + + bool ok = m_impl.insert(std::make_pair(range, toEntry)).second; + CHECK(ok); + + XDKLog("xdk: size=%d moved from=0x%x to=0x%x name=%s\n", + m_impl.size(), from, to, toEntry.getLogLine().c_str()); +} + +} } // namespace xdk::internal diff --git a/src/v8/src/third_party/xdk/xdk-code-map.h b/src/v8/src/third_party/xdk/xdk-code-map.h new file mode 100644 index 0000000..18e3c09 --- /dev/null +++ b/src/v8/src/third_party/xdk/xdk-code-map.h @@ -0,0 +1,134 @@ +// Copyright (c) 2013 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef XDK_CODE_MAP_H_ +#define XDK_CODE_MAP_H_ + +// ---------------------------------------------------------------------------- +// +// This file contains the FunctionSnapshot and related objects declarations +// +// The FunctionSnapshot object maintains a map of JIT compiled functions. +// It is modified on code events(CodeAdded, CodeMoved and CodeDeleted) from +// V8 built-in profiler. +// +// ---------------------------------------------------------------------------- + +#include "xdk-types.h" +#include +#include +#include +#include + +namespace xdk { +namespace internal { + +class LineMap; +typedef std::map< + v8engine::Address, // start address of code + LineMap*> LineMaps; + +// This class is used to record the JITted code position info for JIT +// code profiling. +class LineMap { + public: + struct LineEntry { + LineEntry(size_t offset, size_t line) + : pcOffset(offset), line(line) { } + + size_t pcOffset; // PC offset from the begining of the code trace. + size_t line; // Can be either position returned from V8 assembler + // (which needs to be converted to src line) or src line + // number. + }; + + typedef std::list Entries; + + void setPosition(size_t offset, size_t line) { + addCodeLineEntry(LineEntry(offset, line)); + } + + inline size_t getSize() const { return m_lines.size(); } + const Entries* getEntries() const { return &m_lines; } + + private: + void addCodeLineEntry(const LineEntry& entry) { m_lines.push_back(entry); } + + Entries m_lines; +}; + +// This class describes the function reported with CodeAdded event. +class Function { + public: + explicit Function(v8engine::Address codeAddr, uint32_t codeLen, + const std::string& name, const std::string& type, + const LineMap* lineMap); + + inline v8engine::Address getCodeAddress() const { return m_codeAddr; } + inline uint32_t getCodeLength() const { return m_codeLen; } + + inline const std::string& getType() const { return m_type; } + inline const std::string& getName() const { return m_name; } + inline const std::string& getLogLine() const { return m_logLine; } + + const LineMap& getLineMap() const { return m_lineMap; } + + private: + v8engine::Address m_codeAddr; + uint32_t m_codeLen; + std::string m_name; + std::string m_type; + std::string m_logLine; + LineMap m_lineMap; +}; + +// This class describes the code range related to object of Function type. +// The start address and length are taken from CodeAdded event. +class Range { + public: + class Comparator : public std::binary_function { + public: + bool operator()(const Range& l, const Range& r) const { + return (l.start() + l.length() <= r.start()); + } + }; + + Range(v8engine::Address start, uint32_t length) + : m_start(start), m_length(length) { } + + inline v8engine::Address start() const { return m_start; } + inline uint32_t length() const { return m_length; } + + private: + v8engine::Address m_start; + uint32_t m_length; +}; + +// This class maintains a map of JIT compiled functions. +// The content is changed on CodeAdded, CodeMoved and CodeDeleted events. +typedef std::map CodeMap; + +class FunctionSnapshot { + public: + explicit FunctionSnapshot() {} + virtual ~FunctionSnapshot() { m_impl.clear(); } + + void insert(const Function& func); + void move(v8engine::Address from, v8engine::Address to); + void remove(v8engine::Address addr); + + inline const CodeMap& entries() { return m_impl; } + + private: + FunctionSnapshot(const FunctionSnapshot&); + FunctionSnapshot& operator=(const FunctionSnapshot&); + + void removeAll(const Range& range); + + CodeMap m_impl; +}; + +} } // namespace xdk::internal + +#endif // XDK_CODE_MAP_H_ diff --git a/src/v8/src/third_party/xdk/xdk-types.h b/src/v8/src/third_party/xdk/xdk-types.h new file mode 100644 index 0000000..4fc9fd0 --- /dev/null +++ b/src/v8/src/third_party/xdk/xdk-types.h @@ -0,0 +1,23 @@ +// Copyright (c) 2013 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef XDK_TYPES_H_ +#define XDK_TYPES_H_ + +#include "v8.h" +#include "platform.h" + +namespace v8engine = v8::internal; + +static void XDKLog(const char* msg, ...) { +#if DEBUG + va_list arguments; + va_start(arguments, msg); + v8engine::OS::VPrint(msg, arguments); + va_end(arguments); +#endif +} + +#endif // XDK_TYPES__H_ + diff --git a/src/v8/src/third_party/xdk/xdk-v8.cc b/src/v8/src/third_party/xdk/xdk-v8.cc new file mode 100644 index 0000000..a2ac333 --- /dev/null +++ b/src/v8/src/third_party/xdk/xdk-v8.cc @@ -0,0 +1,32 @@ +// Copyright (c) 2013 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "../../../include/v8.h" +#include "xdk-v8.h" +#include "xdk-agent.h" + +namespace xdk { + +void XDKInitializeForV8(v8::internal::Isolate* isolate) { + if (!internal::XDKAgent::instance().setUp(isolate)) return; + + XDKLog("xdk: XDKInitializeForV8\n"); + + // The --prof flag is requred for now to enable the CPU ticks collection. + // This flag will be removed once xdk agent implements own sampler. + const char* flags = "--prof"; + v8::V8::SetFlagsFromString(flags, static_cast(strlen(flags))); + + v8::V8::SetJitCodeEventHandler(v8::kJitCodeEventDefault, + xdk::internal::EventHandler); + + internal::XDKAgent::instance().Start(); +} + + +bool XDKIsAgentAlive() { + return internal::XDKAgent::instance().isAlive(); +} + +} // namespace xdk diff --git a/src/v8/src/third_party/xdk/xdk-v8.gyp b/src/v8/src/third_party/xdk/xdk-v8.gyp new file mode 100644 index 0000000..10f3b3b --- /dev/null +++ b/src/v8/src/third_party/xdk/xdk-v8.gyp @@ -0,0 +1,42 @@ +# Copyright (c) 2013 Intel Corporation. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'variables': { + 'v8_code': 1, + }, + 'includes': ['../../../build/toolchain.gypi', '../../../build/features.gypi'], + 'targets': [ + { + 'target_name': 'v8_xdk', + 'type': 'static_library', + 'conditions': [ + ['want_separate_host_toolset==1', { + 'toolsets': ['host', 'target'], + }, { + 'toolsets': ['target'], + }], + ], + 'include_dirs+': [ + '../../', + ], + 'sources': [ + 'xdk-v8.h', + 'xdk-v8.cc', + 'xdk-agent.h', + 'xdk-agent.cc', + 'xdk-code-map.h', + 'xdk-code-map.cc', + 'xdk-types.h', + ], + 'direct_dependent_settings': { + 'conditions': [ + ['OS != "win"', { + 'libraries': ['-ldl',], + }], + ], + }, + }, + ] +} diff --git a/src/v8/src/third_party/xdk/xdk-v8.h b/src/v8/src/third_party/xdk/xdk-v8.h new file mode 100644 index 0000000..56e717f --- /dev/null +++ b/src/v8/src/third_party/xdk/xdk-v8.h @@ -0,0 +1,107 @@ +// Copyright (c) 2013 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_XDK_H_ +#define V8_XDK_H_ + +// ---------------------------------------------------------------------------- +// +// XDK profiling support for V8 +// +// SOURCES: +// +// 1. XDK agent source files are located in v8/src/third_party/xdk folder. +// +// To integrate this stuff into V8 build system you need to modify +// two v8 files: +// +// 1. v8/build/features.gypi +// 'v8_enable_xdkprof': 1, +// 2. v8/tools/gyp/v8.gyp +// ['v8_enable_xdkprof==1', { +// 'dependencies': ['../../src/third_party/xdk/xdk-v8.gyp:v8_xdk',], +// }], +// +// 2. Two V8 files v8/src/log.cc and v8/src/log.h need to be modified +// +// The changes related to start CPU ticks collection using V8 built-in +// profiler. +// We are working on reduce these changes up to 2 lines: +// +// #include "third_party/xdk/xdk-v8.h" +// bool Logger::SetUp(Isolate* isolate) { +// ... +// xdk::XDKInitializeForV8(isolate); +// ... +// } +// +// OVERVIEW: +// +// Start up +// +// XDK agent is initialized as a part of V8 built-in profiler on process +// start up. V8 built-in profiler should be paused (CPU ticks are not +// collected). +// +// v8/src/log.cc: +// bool Logger::SetUp(Isolate* isolate) { +// ... +// xdk::XDKInitializeForV8(isolate); +// ... +// } +// +// XDKInitializeForV8() function +// 1. Checks whether XDK agent can be initialized. If a marker file is not +// found that initialization will be discarded. +// 2. Starts a listener thread to accept start / stop profiling command +// from AppAnalyzer (xdk/xdk-agent.cc). +// 3. Registeres a callback to consume the CodeAdded, CodeMoved, +// CodeDeleted events and events related to source line info by +// the agent. +// +// Runtime +// +// XDK profiler consumes the code events (EventHandler() in xdk/xdk-agent.cc) +// V8 emits these events even when CPU ticks collection is paused. +// The profiler uses the code events to maintain a function snapshot (list of +// code ranges assosiated with function name and source line info) +// (xdk-code-map.cc). +// +// Start profiling +// +// When the profiler receives a command to start profiling that it calls +// resumeSampling() (xdk/xdk-agent.cc) which +// 1. Creates a new trace file to log the ticks and code events +// 2. Puts the function snapshot into the trace file +// 3. Resumes CPU ticks collection +// +// Stop profiling +// +// When the profiler receives a command to stop profiling that it calls +// pauseSampling() (xdk/xdk-agent.cc) which stops the CPU ticks collection. +// Note that the agent continues to consume the code events to maintain +// the function snapshot. +// +// When collection is stopped that AppAnalyzer takes the trace file for +// processing. +// +// ---------------------------------------------------------------------------- + +namespace xdk { + +// This function +// - Overrides the V8 flags to specify a new logfile for writting profiling data +// (CPU ticks and Code* events). +// - Registers callback to get line number info and code events from V8 built-in +// profiler. These data are needed to mantain the code map. +// - Starts the XDK agent listener thread which is awaiting for start and stop +// profiling commands. +void XDKInitializeForV8(v8::internal::Isolate* isolate); + +bool XDKIsAgentAlive(); + +} // namespace XDK + +#endif // V8_XDK_H_ + diff --git a/src/v8/tools/check-static-initializers.sh b/src/v8/tools/check-static-initializers.sh index 11ba080..ccebcec 100755 --- a/src/v8/tools/check-static-initializers.sh +++ b/src/v8/tools/check-static-initializers.sh @@ -33,7 +33,10 @@ # - _GLOBAL__I__ZN2v810LineEditor6first_E # - _GLOBAL__I__ZN2v88internal32AtomicOps_Internalx86CPUFeaturesE # - _GLOBAL__I__ZN2v88internal8ThreadId18highest_thread_id_E -expected_static_init_count=3 + +# The XDK CPU profiler patch adds one more static initializer. +# - _GLOBAL__sub_I__ZN3xdk8internal8XDKAgent9instance_E +expected_static_init_count=4 v8_root=$(readlink -f $(dirname $BASH_SOURCE)/../) diff --git a/src/v8/tools/gyp/v8.gyp b/src/v8/tools/gyp/v8.gyp index e4a0416..43d70e1 100644 --- a/src/v8/tools/gyp/v8.gyp +++ b/src/v8/tools/gyp/v8.gyp @@ -611,6 +611,9 @@ }, { 'toolsets': ['target'], }], + ['v8_enable_xdkprof==1', { + 'dependencies': ['../../src/third_party/xdk/xdk-v8.gyp:v8_xdk',], + }], ['v8_target_arch=="arm"', { 'sources': [ ### gcmole(arch:arm) ### '../../src/arm/assembler-arm-inl.h', diff --git a/src/xwalk/DEPS.xwalk b/src/xwalk/DEPS.xwalk index d3137ee..c80c1c5 100644 --- a/src/xwalk/DEPS.xwalk +++ b/src/xwalk/DEPS.xwalk @@ -7,9 +7,9 @@ # Use 'Trunk' for trunk. # If using trunk, will use '.DEPS.git' for gclient. chromium_version = '34.0.1847.45' -chromium_crosswalk_point = 'cb7bc58aa1239e2fa92d692e81ccd06fa6c82722' +chromium_crosswalk_point = 'b83e11e691378bba3b997c6c7667908c4cd33798' blink_crosswalk_point = 'e8b6b995b38b422c2b4d58fa5201599f1e510537' -v8_crosswalk_point = '702dcf9c6e58d90e85594fbe54579ade631fe3b5' +v8_crosswalk_point = 'ffe72f9229923611866423ced472b1dff97abdfe' deps_xwalk = { 'src': 'https://github.com/crosswalk-project/chromium-crosswalk.git@%s' % chromium_crosswalk_point, @@ -17,7 +17,7 @@ deps_xwalk = { 'src/v8': 'https://github.com/crosswalk-project/v8-crosswalk.git@%s' % v8_crosswalk_point, # Ozone-Wayland is required for Wayland support in Chromium. - 'src/ozone': 'https://github.com/01org/ozone-wayland.git@39d93a66777395d5c9ca11da95c436b9192d9725', + 'src/ozone': 'https://github.com/01org/ozone-wayland.git@a4e3e96b1e1c51cecf5f7bc162b0d98064c95bc2', } vars_xwalk = { } diff --git a/src/xwalk/VERSION b/src/xwalk/VERSION index 456d7c8..d2d743c 100644 --- a/src/xwalk/VERSION +++ b/src/xwalk/VERSION @@ -1,4 +1,4 @@ -MAJOR=5 +MAJOR=6 MINOR=34 -BUILD=105 +BUILD=113 PATCH=0 diff --git a/src/xwalk/app/android/app_template/src/org/xwalk/app/template/AppTemplateActivity.java b/src/xwalk/app/android/app_template/src/org/xwalk/app/template/AppTemplateActivity.java index be0c240..f0c556b 100644 --- a/src/xwalk/app/android/app_template/src/org/xwalk/app/template/AppTemplateActivity.java +++ b/src/xwalk/app/android/app_template/src/org/xwalk/app/template/AppTemplateActivity.java @@ -5,7 +5,10 @@ package org.xwalk.app.template; import android.graphics.Color; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; import android.os.Bundle; +import android.view.WindowManager; import android.view.KeyEvent; import android.view.View; import android.widget.TextView; @@ -42,4 +45,26 @@ public class AppTemplateActivity extends XWalkRuntimeActivityBase { setContentView(msgText); } } + + private void enterFullscreen() { + if (VERSION.SDK_INT >= VERSION_CODES.KITKAT && + ((getWindow().getAttributes().flags & + WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0)) { + View decorView = getWindow().getDecorView(); + decorView.setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_STABLE | + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_FULLSCREEN | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + } + } + + public void setIsFullscreen(boolean isFullscreen) { + if (isFullscreen) { + enterFullscreen(); + } + } + } diff --git a/src/xwalk/app/tools/android/customize.py b/src/xwalk/app/tools/android/customize.py index c8eac2e..675b67b 100755 --- a/src/xwalk/app/tools/android/customize.py +++ b/src/xwalk/app/tools/android/customize.py @@ -114,8 +114,8 @@ def CustomizeThemeXML(sanitized_name, fullscreen, launch_screen_img): def CustomizeXML(sanitized_name, package, app_versionCode, app_version, - description, name, orientation, icon, fullscreen, - launch_screen_img, permissions): + description, name, orientation, icon_dict, fullscreen, + launch_screen_img, permissions, app_root): manifest_path = os.path.join(sanitized_name, 'AndroidManifest.xml') if not os.path.isfile(manifest_path): print ('Please make sure AndroidManifest.xml' @@ -143,19 +143,9 @@ def CustomizeXML(sanitized_name, package, app_versionCode, app_version, if orientation: EditElementAttribute(xmldoc, 'activity', 'android:screenOrientation', orientation) - if icon and os.path.isfile(icon): - drawable_path = os.path.join(sanitized_name, 'res', 'drawable') - if not os.path.exists(drawable_path): - os.makedirs(drawable_path) - icon_file = os.path.basename(icon) - icon_file = ReplaceInvalidChars(icon_file) - shutil.copyfile(icon, os.path.join(drawable_path, icon_file)) - icon_name = os.path.splitext(icon_file)[0] - EditElementAttribute(xmldoc, 'application', - 'android:icon', '@drawable/%s' % icon_name) - elif icon and (not os.path.isfile(icon)): - print ('Please make sure the icon file does exist!') - sys.exit(6) + if CustomizeIcon(sanitized_name, app_root, icon_dict): + EditElementAttribute(xmldoc, 'application', 'android:icon', + '@drawable/icon') file_handle = open(os.path.join(sanitized_name, 'AndroidManifest.xml'), 'w') xmldoc.writexml(file_handle, encoding='utf-8') @@ -172,21 +162,21 @@ def ReplaceString(file_path, src, dest): file_handle.close() -def SetVariable(file_path, variable, value): +def SetVariable(file_path, string_line, variable, value): function_string = ('%sset%s(%s);\n' % (' ', variable, value)) temp_file_path = file_path + '.backup' file_handle = open(temp_file_path, 'w+') for line in open(file_path): file_handle.write(line) - if (line.find('public void onCreate(Bundle savedInstanceState)') >= 0): + if (line.find(string_line) >= 0): file_handle.write(function_string) file_handle.close() shutil.move(temp_file_path, file_path) def CustomizeJava(sanitized_name, package, app_url, app_local_path, - enable_remote_debugging): + enable_remote_debugging, display_as_fullscreen): root_path = os.path.join(sanitized_name, 'src', package.replace('.', os.path.sep)) dest_activity = os.path.join(root_path, sanitized_name + 'Activity.java') @@ -214,7 +204,13 @@ def CustomizeJava(sanitized_name, package, app_url, app_local_path, sys.exit(8) if enable_remote_debugging: - SetVariable(dest_activity, 'RemoteDebugging', 'true') + SetVariable(dest_activity, + 'public void onCreate(Bundle savedInstanceState)', + 'RemoteDebugging', 'true') + if display_as_fullscreen: + SetVariable(dest_activity, + 'super.onCreate(savedInstanceState)', + 'IsFullscreen', 'true') def CopyExtensionFile(extension_name, suffix, src_path, dest_path): @@ -337,19 +333,53 @@ def CustomizeExtensions(sanitized_name, name, extensions): extension_json_file.close() +def CustomizeIcon(sanitized_name, app_root, icon_dict): + icon_exist = False + drawable_dict = {'ldpi':[1, 37], 'mdpi':[37, 72], 'hdpi':[72, 96], + 'xhdpi':[96, 120], 'xxhdpi':[120, 144]} + if not icon_dict: + return icon_exist + + try: + icon_dict = dict((int(k), v) for k, v in icon_dict.items()) + except ValueError: + print('The key of icon in the manifest file should be a number.') + + if len(icon_dict) > 0: + icon_list = sorted(icon_dict.iteritems(), key = lambda d:d[0]) + for kd, vd in drawable_dict.iteritems(): + for item in icon_list: + if item[0] >= vd[0] and item[0] < vd[1]: + drawable_path = os.path.join(sanitized_name, 'res', 'drawable-' + kd) + if not os.path.exists(drawable_path): + os.makedirs(drawable_path) + icon = os.path.join(app_root, item[1]) + if icon and os.path.isfile(icon): + icon_name = os.path.basename(icon) + icon_suffix = icon_name.split('.')[-1] + shutil.copyfile(icon, os.path.join(drawable_path, + 'icon.' + icon_suffix)) + icon_exist = True + elif icon and (not os.path.isfile(icon)): + print ('Error: Please make sure \"' + icon + '\" does exist!') + sys.exit(6) + break + return icon_exist + + def CustomizeAll(app_versionCode, description, icon, permissions, app_url, app_root, app_local_path, enable_remote_debugging, - fullscreen_flag, extensions, launch_screen_img, + display_as_fullscreen, extensions, launch_screen_img, package='org.xwalk.app.template', name='AppTemplate', - app_version='1.0.0', orientation='unspecified'): + app_version='1.0.0', orientation='unspecified') : sanitized_name = ReplaceInvalidChars(name, 'apkname') try: Prepare(sanitized_name, package, app_root) CustomizeXML(sanitized_name, package, app_versionCode, app_version, - description, name, orientation, icon, fullscreen_flag, - launch_screen_img, permissions) + description, name, orientation, icon, display_as_fullscreen, + launch_screen_img, permissions, app_root) CustomizeJava(sanitized_name, package, app_url, app_local_path, - enable_remote_debugging) + enable_remote_debugging, display_as_fullscreen) CustomizeExtensions(sanitized_name, name, extensions) except SystemExit as ec: print('Exiting with error code: %d' % ec.code) @@ -370,8 +400,6 @@ def main(): info = ('The application description. Such as:' '--description=YourApplicationdDescription') parser.add_option('--description', help=info) - info = ('The path of icon. Such as: --icon=/path/to/your/customized/icon') - parser.add_option('--icon', help=info) info = ('The permission list. Such as: --permissions="geolocation"' 'For more permissions, such as:' '--permissions="geolocation:permission2"') @@ -408,7 +436,21 @@ def main(): help='The fallback image for launch_screen') options, _ = parser.parse_args() try: - CustomizeAll(options.app_versionCode, options.description, options.icon, + icon_dict = {144: 'icons/icon_144.png', + 72: 'icons/icon_72.png', + 96: 'icons/icon_96.png', + 48: 'icons/icon_48.png'} + if options.name == None: + options.name = 'Example' + if options.app_root == None: + options.app_root = os.path.join('test_data', 'manifest') + if options.package == None: + options.package = 'org.xwalk.app.template' + if options.orientation == None: + options.orientation = 'unspecified' + if options.app_version == None: + options.app_version = '1.0.0' + CustomizeAll(options.app_versionCode, options.description, icon_dict, options.permissions, options.app_url, options.app_root, options.app_local_path, options.enable_remote_debugging, options.fullscreen, options.extensions, diff --git a/src/xwalk/app/tools/android/make_apk.py b/src/xwalk/app/tools/android/make_apk.py index f096cdb..8c4901f 100755 --- a/src/xwalk/app/tools/android/make_apk.py +++ b/src/xwalk/app/tools/android/make_apk.py @@ -111,15 +111,7 @@ def ParseManifest(options): sys.exit(9) if parser.GetAppRoot(): options.app_root = parser.GetAppRoot() - temp_dict = parser.GetIcons() - try: - icon_dict = dict((int(k), v) for k, v in temp_dict.items()) - except ValueError: - print('The key of icon in the manifest file should be a number.') - # TODO(junmin): add multiple icons support. - if icon_dict: - icon_file = max(iter(icon_dict.items()), key=operator.itemgetter(0))[1] - options.icon = os.path.join(options.app_root, icon_file) + options.icon_dict = parser.GetIcons() if parser.GetFullScreenFlag().lower() == 'true': options.fullscreen = True elif parser.GetFullScreenFlag().lower() == 'false': @@ -195,13 +187,10 @@ def Customize(options): name = 'AppTemplate' if options.name: name = options.name - app_version = '1.0.0' + app_version = '' if options.app_version: app_version = options.app_version app_versionCode = MakeVersionCode(options) - icon = '' - if options.icon: - icon = os.path.expanduser(options.icon) app_root = '' if options.app_root: app_root = os.path.expanduser(options.app_root) @@ -214,7 +203,7 @@ def Customize(options): orientation = 'unspecified' if options.orientation: orientation = options.orientation - CustomizeAll(app_versionCode, options.description, icon, + CustomizeAll(app_versionCode, options.description, options.icon_dict, options.permissions, options.app_url, app_root, options.app_local_path, remote_debugging, fullscreen_flag, options.extensions, @@ -702,18 +691,24 @@ def main(argv): if options.permissions: permission_list = options.permissions.split(':') else: - print ('Warning: all supported permissions on Android port are added. ' - 'Refer to https://github.com/crosswalk-project/' - 'crosswalk-website/wiki/Crosswalk-manifest') + print('Warning: all supported permissions on Android port are added. ' + 'Refer to https://github.com/crosswalk-project/' + 'crosswalk-website/wiki/Crosswalk-manifest') permission_list = permission_mapping_table.keys() options.permissions = HandlePermissionList(permission_list) - + options.icon_dict = {} else: try: ParseManifest(options) except SystemExit as ec: return ec.code + if (options.app_root and options.app_local_path and not + os.path.isfile(os.path.join(options.app_root, options.app_local_path))): + print('Please make sure that the local path file of launching app ' + 'does exist.') + sys.exit(7) + options.name = ReplaceInvalidChars(options.name, 'apkname') options.package = ReplaceInvalidChars(options.package) sanitized_name = ReplaceInvalidChars(options.name, 'apkname') diff --git a/src/xwalk/app/tools/android/make_apk_test.py b/src/xwalk/app/tools/android/make_apk_test.py index ffed605..e49e15a 100755 --- a/src/xwalk/app/tools/android/make_apk_test.py +++ b/src/xwalk/app/tools/android/make_apk_test.py @@ -270,6 +270,19 @@ class TestMakeApk(unittest.TestCase): self.checkApks('Example', '1.0.0') Clean('Example', '1.0.0') + def testPermissionsWithError(self): + cmd = ['python', 'make_apk.py', '--name=Example', '--app-version=1.0.0', + '--package=org.xwalk.example', '--permissions=UndefinedPermission', + '--app-url=http://www.intel.com', self._mode] + out = RunCommand(cmd) + self.assertTrue(out.find('related API is not supported.') != -1) + cmd = ['python', 'make_apk.py', '--name=Example', '--app-version=1.0.0', + '--package=org.xwalk.example', + '--permissions=Contacts.Geolocation.Messaging', + '--app-url=http://www.intel.com', self._mode] + out = RunCommand(cmd) + self.assertTrue(out.find('related API is not supported.') != -1) + def testPackage(self): cmd = ['python', 'make_apk.py', '--name=Example', '--app-version=1.0.0', self._mode] @@ -331,17 +344,24 @@ class TestMakeApk(unittest.TestCase): self.assertFalse(os.path.exists('Example.apk')) Clean('Example', '1.0.0') + manifest_path = os.path.join('test_data', 'manifest', + 'manifest_app_launch_local_path.json') + cmd = ['python', 'make_apk.py', '--manifest=%s' % manifest_path, self._mode] + out = RunCommand(cmd) + self.assertTrue(out.find('Please make sure that the local path file') != -1) + self.assertFalse(os.path.exists('Example.apk')) + Clean('Example', '1.0.0') + def testIcon(self): - icon_path = './app_src/res/drawable-xhdpi/crosswalk.png' + manifest_path = os.path.join('test_data', 'manifest', 'manifest_icon.json') cmd = ['python', 'make_apk.py', '--name=Example', '--app-version=1.0.0', '--package=org.xwalk.example', '--app-url=http://www.intel.com', - '--icon=%s' % icon_path, self._mode] + '--manifest=%s' % manifest_path, self._mode] RunCommand(cmd) manifest = 'Example/AndroidManifest.xml' with open(manifest, 'r') as content_file: content = content_file.read() - self.assertTrue(content.find('crosswalk') != -1) - self.assertTrue(os.path.exists('Example/res/drawable')) + self.assertTrue(content.find('drawable/icon') != -1) self.checkApks('Example', '1.0.0') Clean('Example', '1.0.0') @@ -602,8 +622,10 @@ class TestMakeApk(unittest.TestCase): icon_path = './app_src/res/drawable-xhdpi/crosswalk.png' extension_path = 'test_data/extensions/myextension' arch = '' + icon = '' if exec_file.find("make_apk.py") != -1: arch = '--arch=x86' + icon = '--icon=%s' % icon_path cmd = ['python', '%s' % exec_file, '--app-version=1.0.0', '--app-url=http://www.intel.com', @@ -612,7 +634,7 @@ class TestMakeApk(unittest.TestCase): '--enable-remote-debugging', '--extensions=%s' % extension_path, '--fullscreen', - '--icon=%s' % icon_path, + '%s' % icon, '--name=Example', '--orientation=landscape', '--package=org.xwalk.example', @@ -637,8 +659,6 @@ class TestMakeApk(unittest.TestCase): self.assertTrue(content.find('versionName') != -1) # Test orientation option. self.assertTrue(content.find('landscape') != -1) - # Test icon option. - self.assertTrue(os.path.exists('Example/res/drawable')) # Test fullscreen option theme = 'Example/res/values/theme.xml' with open(theme, 'r') as content_file: @@ -730,6 +750,7 @@ def SuiteWithModeOption(): test_suite.addTest(TestMakeApk('testOrientation')) test_suite.addTest(TestMakeApk('testPackage')) test_suite.addTest(TestMakeApk('testPermissions')) + test_suite.addTest(TestMakeApk('testPermissionsWithError')) test_suite.addTest(TestMakeApk('testXPK')) test_suite.addTest(TestMakeApk('testXPKWithError')) test_suite.addTest(TestMakeApk('testTargetDir')) diff --git a/src/xwalk/app/tools/android/manifest_json_parser.py b/src/xwalk/app/tools/android/manifest_json_parser.py index 3a1f281..9f61231 100755 --- a/src/xwalk/app/tools/android/manifest_json_parser.py +++ b/src/xwalk/app/tools/android/manifest_json_parser.py @@ -134,10 +134,10 @@ class ManifestJsonParser(object): ret_dict['plugin'] = '' if 'plugin' in self.data_src: ret_dict['plugin'] = self.data_src['plugin'] - if 'fullscreen' in self.data_src: - ret_dict['fullscreen'] = self.data_src['fullscreen'] + if 'display' in self.data_src and 'fullscreen' in self.data_src['display']: + ret_dict['fullscreen'] = 'true' else: - ret_dict['fullscreen'] = 'False' + ret_dict['fullscreen'] = '' ret_dict['launch_screen_img'] = '' if 'launch_screen' in self.data_src: if 'default' not in self.data_src['launch_screen']: diff --git a/src/xwalk/app/tools/android/test_data/manifest/icons/icon_144.png b/src/xwalk/app/tools/android/test_data/manifest/icons/icon_144.png new file mode 100644 index 0000000..b69b4e8 Binary files /dev/null and b/src/xwalk/app/tools/android/test_data/manifest/icons/icon_144.png differ diff --git a/src/xwalk/app/tools/android/test_data/manifest/icons/icon_48.png b/src/xwalk/app/tools/android/test_data/manifest/icons/icon_48.png new file mode 100644 index 0000000..e87864a Binary files /dev/null and b/src/xwalk/app/tools/android/test_data/manifest/icons/icon_48.png differ diff --git a/src/xwalk/app/tools/android/test_data/manifest/icons/icon_72.png b/src/xwalk/app/tools/android/test_data/manifest/icons/icon_72.png new file mode 100644 index 0000000..fc19a3e Binary files /dev/null and b/src/xwalk/app/tools/android/test_data/manifest/icons/icon_72.png differ diff --git a/src/xwalk/app/tools/android/test_data/manifest/icons/icon_96.png b/src/xwalk/app/tools/android/test_data/manifest/icons/icon_96.png new file mode 100644 index 0000000..5989755 Binary files /dev/null and b/src/xwalk/app/tools/android/test_data/manifest/icons/icon_96.png differ diff --git a/src/xwalk/app/tools/android/test_data/manifest/manifest.json b/src/xwalk/app/tools/android/test_data/manifest/manifest.json index db02b81..fb355be 100644 --- a/src/xwalk/app/tools/android/test_data/manifest/manifest.json +++ b/src/xwalk/app/tools/android/test_data/manifest/manifest.json @@ -18,5 +18,5 @@ "Messaging"], "required_version": "1.28.1.0", "plugin": [], - "fullscreen":"true" + "display": ["fullscreen"] } diff --git a/src/xwalk/app/tools/android/test_data/manifest/manifest_app_launch_local_path.json b/src/xwalk/app/tools/android/test_data/manifest/manifest_app_launch_local_path.json index 05e4d2c..1900c70 100644 --- a/src/xwalk/app/tools/android/test_data/manifest/manifest_app_launch_local_path.json +++ b/src/xwalk/app/tools/android/test_data/manifest/manifest_app_launch_local_path.json @@ -14,5 +14,5 @@ "permissions": ["geolocation"], "required_version": "1.28.1.0", "plugin": [], - "fullscreen":"true" + "display": ["fullscreen"] } diff --git a/src/xwalk/app/tools/android/test_data/manifest/manifest_icon.json b/src/xwalk/app/tools/android/test_data/manifest/manifest_icon.json new file mode 100644 index 0000000..44a8274 --- /dev/null +++ b/src/xwalk/app/tools/android/test_data/manifest/manifest_icon.json @@ -0,0 +1,20 @@ +{ + "name": "Example", + "version": "1.0.0", + "launch_path": "http://www.intel.com", + "app": { + "launch": { + "local_path": "index.html" + } + }, + "description": "a sample description", + "origin": "app://app.id", + "icons": { + "144": "icons/icon_144.png", + "96": "icons/icon_96.png", + "72": "icons/icon_72.png", + "48": "icons/icon_48.png" + }, + "default_locale": "en", + "fullscreen":"true" +} diff --git a/src/xwalk/application/application_resources.grd b/src/xwalk/application/application_resources.grd index a3edb37..c02ece3 100644 --- a/src/xwalk/application/application_resources.grd +++ b/src/xwalk/application/application_resources.grd @@ -13,6 +13,7 @@ + diff --git a/src/xwalk/application/browser/application.cc b/src/xwalk/application/browser/application.cc index 940a4fd..79e654e 100644 --- a/src/xwalk/application/browser/application.cc +++ b/src/xwalk/application/browser/application.cc @@ -22,10 +22,12 @@ #include "xwalk/application/common/application_manifest_constants.h" #include "xwalk/application/common/constants.h" #include "xwalk/application/common/manifest_handlers/main_document_handler.h" +#include "xwalk/application/common/manifest_handlers/warp_handler.h" #include "xwalk/application/common/event_names.h" #include "xwalk/runtime/browser/runtime.h" #include "xwalk/runtime/browser/runtime_context.h" #include "xwalk/runtime/browser/xwalk_runner.h" +#include "xwalk/runtime/common/xwalk_common_messages.h" namespace xwalk { @@ -39,6 +41,11 @@ const char* kDefaultWidgetEntryPage[] = { "index.svg", "index.xhtml", "index.xht"}; + +content::RenderProcessHost* GetHost(Runtime* runtime) { + DCHECK(runtime); + return runtime->web_contents()->GetRenderProcessHost(); +} } // namespace namespace application { @@ -97,6 +104,7 @@ bool Application::Launch(const LaunchParams& launch_params) { return false; main_runtime_ = Runtime::Create(runtime_context_, this); + InitSecurityPolicy(); main_runtime_->LoadURL(url); if (entry_point_used_ != AppMainKey) { NativeAppWindow::CreateParams params; @@ -382,5 +390,46 @@ bool Application::SetPermission(PermissionType type, return false; } +void Application::InitSecurityPolicy() { + if (application_data_->GetPackageType() != Manifest::TYPE_WGT) + return; + const WARPInfo* info = static_cast( + application_data_->GetManifestData(widget_keys::kAccessKey)); + if (!info +#if defined(OS_TIZEN) + // On Tizen, CSP mode has higher priority, and WARP will be disabled + // if the application is under CSP mode. + || application_data_->HasCSPDefined() +#endif + ) + return; + GURL app_url = application_data_->URL(); + const base::ListValue* whitelist = info->GetWARP(); + bool enable_warp_mode = true; + for (base::ListValue::const_iterator it = whitelist->begin(); + it != whitelist->end(); ++it) { + base::DictionaryValue* value = NULL; + (*it)->GetAsDictionary(&value); + std::string dest; + if (!value || !value->GetString(widget_keys::kAccessOriginKey, &dest) || + dest.empty()) + continue; + if (dest == "*") { + enable_warp_mode = false; + break; + } + + GURL dest_url(dest); + // The default subdomains attrubute should be "false". + std::string subdomains = "false"; + value->GetString(widget_keys::kAccessSubdomainsKey, &subdomains); + GetHost(main_runtime_)->Send( + new ViewMsg_SetAccessWhiteList( + app_url, dest_url, (subdomains == "true"))); + } + if (enable_warp_mode) + GetHost(main_runtime_)->Send(new ViewMsg_EnableWarpMode()); +} + } // namespace application } // namespace xwalk diff --git a/src/xwalk/application/browser/application.h b/src/xwalk/application/browser/application.h index e0f2662..f928494 100644 --- a/src/xwalk/application/browser/application.h +++ b/src/xwalk/application/browser/application.h @@ -155,6 +155,8 @@ class Application : public Runtime::Observer { bool IsOnSuspendHandlerRegistered() const; bool IsTerminating() const { return finish_observer_; } + void InitSecurityPolicy(); + RuntimeContext* runtime_context_; const scoped_refptr application_data_; Runtime* main_runtime_; diff --git a/src/xwalk/application/browser/application_service.cc b/src/xwalk/application/browser/application_service.cc index a2ee3f0..1469114 100644 --- a/src/xwalk/application/browser/application_service.cc +++ b/src/xwalk/application/browser/application_service.cc @@ -11,6 +11,7 @@ #include "base/file_util.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" +#include "base/path_service.h" #include "base/version.h" #include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" @@ -26,6 +27,7 @@ #include "xwalk/runtime/browser/runtime_context.h" #include "xwalk/runtime/browser/runtime.h" #include "xwalk/runtime/browser/xwalk_runner.h" +#include "xwalk/runtime/common/xwalk_paths.h" #if defined(OS_TIZEN) #include "xwalk/application/browser/installer/tizen/service_package_installer.h" @@ -187,6 +189,16 @@ bool CopyDirectoryContents(const base::FilePath& from, return true; } +void RemoveWidgetStorageFiles(const base::FilePath& storage_path, + const std::string& app_id) { + base::FileEnumerator iter(storage_path, true, + base::FileEnumerator::FILES); + for (base::FilePath file = iter.Next(); !file.empty(); file = iter.Next()) { + if (file.MaybeAsASCII().find(app_id) != std::string::npos) + base::DeleteFile(file, false); + } +} + } // namespace const base::FilePath::CharType kApplicationsDir[] = @@ -462,6 +474,10 @@ bool ApplicationService::Uninstall(const std::string& id) { application->URL(), partition->GetURLRequestContext()); + base::FilePath path; + PathService::Get(xwalk::DIR_WGT_STORAGE_PATH, &path); + RemoveWidgetStorageFiles(path, id); + FOR_EACH_OBSERVER(Observer, observers_, OnApplicationUninstalled(id)); return result; @@ -499,6 +515,7 @@ Application* ApplicationService::Launch( Application* ApplicationService::Launch( const std::string& id, const Application::LaunchParams& params) { + Application* application = NULL; scoped_refptr application_data = application_storage_->GetApplicationData(id); if (!application_data) { @@ -506,11 +523,17 @@ Application* ApplicationService::Launch( return NULL; } - return Launch(application_data, params); + if ((application = Launch(application_data, params))) { + scoped_refptr event = Event::CreateEvent( + kOnLaunched, scoped_ptr(new base::ListValue)); + event_manager_->SendEvent(application->id(), event); + } + return application; } Application* ApplicationService::Launch( const base::FilePath& path, const Application::LaunchParams& params) { + Application* application = NULL; if (!base::DirectoryExists(path)) return NULL; @@ -524,7 +547,12 @@ Application* ApplicationService::Launch( return NULL; } - return Launch(application_data, params); + if ((application = Launch(application_data, params))) { + scoped_refptr event = Event::CreateEvent( + kOnLaunched, scoped_ptr(new base::ListValue)); + event_manager_->SendEvent(application->id(), event); + } + return application; } namespace { diff --git a/src/xwalk/application/browser/application_system.cc b/src/xwalk/application/browser/application_system.cc index 098ab66..bfc9ce3 100644 --- a/src/xwalk/application/browser/application_system.cc +++ b/src/xwalk/application/browser/application_system.cc @@ -126,9 +126,7 @@ bool ApplicationSystem::LaunchWithCommandLineParam( if (cmd_line.HasSwitch(switches::kFullscreen)) launch_params.window_state = ui::SHOW_STATE_FULLSCREEN; - if (Application* application = - application_service_->Launch(param, launch_params)) { - event_manager_->SendEvent(application->id(), event); + if (application_service_->Launch(param, launch_params)) { return true; } @@ -162,7 +160,7 @@ bool ApplicationSystem::LaunchWithCommandLineParam( if (!application_data) { LOG(ERROR) << "Error occurred while trying to launch application: " << error; - return NULL; + return false; } Application::LaunchParams launch_params; diff --git a/src/xwalk/application/browser/installer/package.cc b/src/xwalk/application/browser/installer/package.cc index 4f73dcd..46285a5 100644 --- a/src/xwalk/application/browser/installer/package.cc +++ b/src/xwalk/application/browser/installer/package.cc @@ -18,7 +18,8 @@ namespace application { Package::Package(const base::FilePath& source_path) : source_path_(source_path), - is_extracted_(false) { + is_extracted_(false), + is_valid_(false) { } Package::~Package() { @@ -46,11 +47,6 @@ bool Package::Extract(base::FilePath* target_path) { return true; } - if (!IsValid()) { - LOG(ERROR) << "XPK/WGT file is not valid."; - return false; - } - if (!CreateTempDirectory()) { LOG(ERROR) << "Can't create a temporary" "directory for extracting the package content."; diff --git a/src/xwalk/application/browser/installer/package.h b/src/xwalk/application/browser/installer/package.h index fe58b98..c946844 100644 --- a/src/xwalk/application/browser/installer/package.h +++ b/src/xwalk/application/browser/installer/package.h @@ -29,7 +29,7 @@ class Package { static scoped_ptr Create(const base::FilePath& path); // The function will unzip the XPK/WGT file and return the target path where // to decompress by the parameter |target_path|. - bool Extract(base::FilePath* target_path); + virtual bool Extract(base::FilePath* target_path); protected: explicit Package(const base::FilePath& source_path); scoped_ptr file_; diff --git a/src/xwalk/application/browser/installer/wgt_package.cc b/src/xwalk/application/browser/installer/wgt_package.cc index 9816abe..9ce643d 100644 --- a/src/xwalk/application/browser/installer/wgt_package.cc +++ b/src/xwalk/application/browser/installer/wgt_package.cc @@ -55,6 +55,8 @@ WGTPackage::WGTPackage(const base::FilePath& path) if (!value.empty()) id_ = GenerateId(value); + is_valid_ = true; + scoped_ptr file( new ScopedStdioHandle(base::OpenFile(path, "rb"))); @@ -63,6 +65,3 @@ WGTPackage::WGTPackage(const base::FilePath& path) } // namespace application } // namespace xwalk - - - diff --git a/src/xwalk/application/browser/installer/xpk_package.cc b/src/xwalk/application/browser/installer/xpk_package.cc index 22cdb33..7c084ac 100644 --- a/src/xwalk/application/browser/installer/xpk_package.cc +++ b/src/xwalk/application/browser/installer/xpk_package.cc @@ -88,5 +88,19 @@ bool XPKPackage::VerifySignature() { return true; } +bool XPKPackage::Extract(base::FilePath* target_path) { + if (is_extracted_) { + *target_path = temp_dir_.path(); + return true; + } + + if (!IsValid()) { + LOG(ERROR) << "The XPK file is not valid."; + return false; + } + + return Package::Extract(target_path); +} + } // namespace application } // namespace xwalk diff --git a/src/xwalk/application/browser/installer/xpk_package.h b/src/xwalk/application/browser/installer/xpk_package.h index 8b96e2f..c7a32a4 100644 --- a/src/xwalk/application/browser/installer/xpk_package.h +++ b/src/xwalk/application/browser/installer/xpk_package.h @@ -30,6 +30,7 @@ class XPKPackage : public Package { }; virtual ~XPKPackage(); explicit XPKPackage(const base::FilePath& path); + virtual bool Extract(base::FilePath* target_path); private: // verify the signature in the xpk package diff --git a/src/xwalk/application/common/application_data.cc b/src/xwalk/application/common/application_data.cc index dea676d..aeec0c1 100644 --- a/src/xwalk/application/common/application_data.cc +++ b/src/xwalk/application/common/application_data.cc @@ -137,7 +137,10 @@ const std::string& ApplicationData::ID() const { } const std::string ApplicationData::VersionString() const { - return Version()->GetString(); + if (!version_->components().empty()) + return Version()->GetString(); + + return ""; } bool ApplicationData::IsPlatformApp() const { @@ -360,5 +363,13 @@ PermissionSet ApplicationData::GetManifestPermissions() const { return permissions; } +#if defined(OS_TIZEN) +bool ApplicationData::HasCSPDefined() const { + return (manifest_->HasPath(widget_keys::kCSPKey) || + manifest_->HasPath(widget_keys::kCSPReportOnlyKey) || + manifest_->HasPath(widget_keys::kAllowNavigationKey)); +} +#endif + } // namespace application } // namespace xwalk diff --git a/src/xwalk/application/common/application_data.h b/src/xwalk/application/common/application_data.h index 34612ee..28d732c 100644 --- a/src/xwalk/application/common/application_data.h +++ b/src/xwalk/application/common/application_data.h @@ -134,6 +134,10 @@ class ApplicationData : public base::RefCountedThreadSafe { bool HasMainDocument() const; Manifest::PackageType GetPackageType() const; +#if defined(OS_TIZEN) + bool HasCSPDefined() const; +#endif + private: friend class base::RefCountedThreadSafe; friend class ApplicationStorageImpl; diff --git a/src/xwalk/application/common/application_manifest_constants.cc b/src/xwalk/application/common/application_manifest_constants.cc index 22f90e1..b0795df 100644 --- a/src/xwalk/application/common/application_manifest_constants.cc +++ b/src/xwalk/application/common/application_manifest_constants.cc @@ -13,6 +13,7 @@ const char kAppMainScriptsKey[] = "app.main.scripts"; const char kAppMainSourceKey[] = "app.main.source"; const char kCSPKey[] = "content_security_policy"; const char kDescriptionKey[] = "description"; +const char kDisplay[] = "display"; const char kLaunchLocalPathKey[] = "app.launch.local_path"; const char kLaunchScreen[] = "launch_screen"; const char kLaunchScreenReadyWhen[] = "launch_screen.ready_when"; @@ -33,6 +34,7 @@ const char kIcon128Key[] = "icons.128"; // manifest keys for widget applications. namespace application_widget_keys { +const char kNamespaceKey[] = "@namespace"; const char kNameKey[] = "widget.name.#text"; const char kVersionKey[] = "widget.@version"; const char kWidgetKey[] = "widget"; @@ -61,10 +63,23 @@ const char kAccessSubdomainsKey[] = "@subdomains"; #if defined(OS_TIZEN) const char kIcon128Key[] = "widget.icon.@src"; +const char kTizenApplicationKey[] = "widget.application"; +// Child keys inside 'kTizenApplicationKey' +const char kTizenApplicationIdKey[] = "@id"; +const char kTizenApplicationPackageKey[] = "@package"; +const char kTizenApplicationRequiredVersionKey[] = "@required_version"; + const char kTizenAppIdKey[] = "widget.application.@package"; +const char kAllowNavigationKey[] = "widget.allow-navigation.#text"; +const char kCSPReportOnlyKey[] = + "widget.content-security-policy-report-only.#text"; #endif } // namespace application_widget_keys +#if defined(OS_TIZEN) +const char kTizenNamespacePrefix[] = "http://tizen.org/ns/widgets"; +#endif + namespace application_manifest_errors { const char kInvalidDescription[] = "Invalid value for 'description'."; diff --git a/src/xwalk/application/common/application_manifest_constants.h b/src/xwalk/application/common/application_manifest_constants.h index 59f5444..406c0dd 100644 --- a/src/xwalk/application/common/application_manifest_constants.h +++ b/src/xwalk/application/common/application_manifest_constants.h @@ -15,6 +15,7 @@ namespace application_manifest_keys { extern const char kAppMainSourceKey[]; extern const char kCSPKey[]; extern const char kDescriptionKey[]; + extern const char kDisplay[]; extern const char kLaunchLocalPathKey[]; extern const char kLaunchScreen[]; extern const char kLaunchScreenReadyWhen[]; @@ -33,6 +34,7 @@ namespace application_manifest_keys { } // namespace application_manifest_keys namespace application_widget_keys { + extern const char kNamespaceKey[]; extern const char kNameKey[]; extern const char kLaunchLocalPathKey[]; extern const char kWebURLsKey[]; @@ -55,11 +57,21 @@ namespace application_widget_keys { extern const char kPreferencesValueKey[]; extern const char kPreferencesReadonlyKey[]; #if defined(OS_TIZEN) + extern const char kTizenApplicationKey[]; + extern const char kTizenApplicationIdKey[]; + extern const char kTizenApplicationPackageKey[]; + extern const char kTizenApplicationRequiredVersionKey[]; extern const char kTizenAppIdKey[]; extern const char kIcon128Key[]; + extern const char kAllowNavigationKey[]; + extern const char kCSPReportOnlyKey[]; #endif } // namespace application_widget_keys +#if defined(OS_TIZEN) +extern const char kTizenNamespacePrefix[]; +#endif + namespace application_manifest_errors { extern const char kInvalidDescription[]; extern const char kInvalidKey[]; diff --git a/src/xwalk/application/common/manifest_handler.cc b/src/xwalk/application/common/manifest_handler.cc index c7395c2..d1b3e32 100644 --- a/src/xwalk/application/common/manifest_handler.cc +++ b/src/xwalk/application/common/manifest_handler.cc @@ -9,6 +9,10 @@ #include "base/stl_util.h" #include "xwalk/application/common/manifest_handlers/csp_handler.h" #include "xwalk/application/common/manifest_handlers/main_document_handler.h" +#if defined(OS_TIZEN) +#include "xwalk/application/common/manifest_handlers/navigation_handler.h" +#include "xwalk/application/common/manifest_handlers/tizen_application_handler.h" +#endif #include "xwalk/application/common/manifest_handlers/permissions_handler.h" #include "xwalk/application/common/manifest_handlers/warp_handler.h" #include "xwalk/application/common/manifest_handlers/widget_handler.h" @@ -71,6 +75,8 @@ ManifestHandlerRegistry::GetInstanceForWGT() { handlers.push_back(new WARPHandler); #if defined(OS_TIZEN) handlers.push_back(new CSPHandler(Manifest::TYPE_WGT)); + handlers.push_back(new NavigationHandler); + handlers.push_back(new TizenApplicationHandler); #endif widget_registry_ = new ManifestHandlerRegistry(handlers); return widget_registry_; diff --git a/src/xwalk/application/common/manifest_handlers/navigation_handler.cc b/src/xwalk/application/common/manifest_handlers/navigation_handler.cc new file mode 100644 index 0000000..3a75474 --- /dev/null +++ b/src/xwalk/application/common/manifest_handlers/navigation_handler.cc @@ -0,0 +1,56 @@ +// Copyright (c) 2014 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "xwalk/application/common/manifest_handlers/navigation_handler.h" + +#include "base/strings/utf_string_conversions.h" +#include "xwalk/application/common/application_manifest_constants.h" + +namespace xwalk { + +namespace keys = application_widget_keys; + +namespace application { +namespace { +const char navigation_separator = ' '; +} + +NavigationInfo::NavigationInfo(const std::string& allowed_domains) { + base::SplitString(allowed_domains, navigation_separator, &allowed_domains_); +} + +NavigationInfo::~NavigationInfo() { +} + +NavigationHandler::NavigationHandler() { +} + +NavigationHandler::~NavigationHandler() { +} + +bool NavigationHandler::Parse(scoped_refptr application_data, + base::string16* error) { + if (!application_data->GetManifest()->HasPath(keys::kAllowNavigationKey)) + return true; + std::string allowed_domains; + if (!application_data->GetManifest()->GetString(keys::kAllowNavigationKey, + &allowed_domains)) { + *error = base::ASCIIToUTF16("Invalid value of allow-navigation."); + return false; + } + if (allowed_domains.empty()) + return true; + + application_data->SetManifestData(keys::kAllowNavigationKey, + new NavigationInfo(allowed_domains)); + + return true; +} + +std::vector NavigationHandler::Keys() const { + return std::vector(1, keys::kAllowNavigationKey); +} + +} // namespace application +} // namespace xwalk diff --git a/src/xwalk/application/common/manifest_handlers/navigation_handler.h b/src/xwalk/application/common/manifest_handlers/navigation_handler.h new file mode 100644 index 0000000..c643382 --- /dev/null +++ b/src/xwalk/application/common/manifest_handlers/navigation_handler.h @@ -0,0 +1,45 @@ +// Copyright (c) 2014 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#ifndef XWALK_APPLICATION_COMMON_MANIFEST_HANDLERS_NAVIGATION_HANDLER_H_ +#define XWALK_APPLICATION_COMMON_MANIFEST_HANDLERS_NAVIGATION_HANDLER_H_ + +#include +#include + +#include "base/strings/string_split.h" +#include "xwalk/application/common/application_data.h" +#include "xwalk/application/common/manifest_handler.h" + +namespace xwalk { +namespace application { + +class NavigationInfo : public ApplicationData::ManifestData { + public: + explicit NavigationInfo(const std::string& allowed_domains); + virtual ~NavigationInfo(); + + const std::vector& GetAllowedDomains() const { + return allowed_domains_; } + + private: + std::vector allowed_domains_; +}; + +class NavigationHandler : public ManifestHandler { + public: + NavigationHandler(); + virtual ~NavigationHandler(); + + virtual bool Parse(scoped_refptr application_data, + base::string16* error) OVERRIDE; + virtual std::vector Keys() const OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(NavigationHandler); +}; + +} // namespace application +} // namespace xwalk + +#endif // XWALK_APPLICATION_COMMON_MANIFEST_HANDLERS_NAVIGATION_HANDLER_H_ diff --git a/src/xwalk/application/common/manifest_handlers/navigation_handler_unittest.cc b/src/xwalk/application/common/manifest_handlers/navigation_handler_unittest.cc new file mode 100644 index 0000000..fc7aab4 --- /dev/null +++ b/src/xwalk/application/common/manifest_handlers/navigation_handler_unittest.cc @@ -0,0 +1,74 @@ +// Copyright (c) 2014 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "xwalk/application/common/manifest_handlers/navigation_handler.h" + +#include +#include +#include "xwalk/application/common/application_manifest_constants.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace xwalk { + +namespace keys = application_widget_keys; + +namespace application { + +class NavigationHandlerTest: public testing::Test { + public: + virtual void SetUp() OVERRIDE { + manifest.SetString(keys::kNameKey, "no name"); + manifest.SetString(keys::kVersionKey, "0"); + } + + scoped_refptr CreateApplication() { + std::string error; + scoped_refptr application = ApplicationData::Create( + base::FilePath(), Manifest::INVALID_TYPE, manifest, "", &error); + return application; + } + + const NavigationInfo* GetNavigationInfo( + scoped_refptr application) { + const NavigationInfo* info = static_cast( + application->GetManifestData(keys::kAllowNavigationKey)); + return info; + } + + base::DictionaryValue manifest; +}; + +TEST_F(NavigationHandlerTest, NoNavigation) { + scoped_refptr application = CreateApplication(); + EXPECT_TRUE(application.get()); + EXPECT_FALSE(GetNavigationInfo(application)); +} + +TEST_F(NavigationHandlerTest, OneNavigation) { + manifest.SetString(keys::kAllowNavigationKey, "http://www.sample.com"); + scoped_refptr application = CreateApplication(); + EXPECT_TRUE(application.get()); + EXPECT_EQ(application->GetPackageType(), Manifest::TYPE_WGT); + const NavigationInfo* info = GetNavigationInfo(application); + EXPECT_TRUE(info); + const std::vector& list = info->GetAllowedDomains(); + EXPECT_TRUE(list.size() == 1 && list[0] == "http://www.sample.com"); +} + +TEST_F(NavigationHandlerTest, Navigations) { + manifest.SetString(keys::kAllowNavigationKey, + "http://www.sample1.com www.sample2.com"); + scoped_refptr application = CreateApplication(); + EXPECT_TRUE(application.get()); + EXPECT_EQ(application->GetPackageType(), Manifest::TYPE_WGT); + const NavigationInfo* info = GetNavigationInfo(application); + EXPECT_TRUE(info); + const std::vector& list = info->GetAllowedDomains(); + EXPECT_TRUE(list.size() == 2 && + list[0] == "http://www.sample1.com" && + list[1] == "www.sample2.com"); +} + +} // namespace application +} // namespace xwalk diff --git a/src/xwalk/application/common/manifest_handlers/tizen_application_handler.cc b/src/xwalk/application/common/manifest_handlers/tizen_application_handler.cc new file mode 100644 index 0000000..1eab83c --- /dev/null +++ b/src/xwalk/application/common/manifest_handlers/tizen_application_handler.cc @@ -0,0 +1,119 @@ +// Copyright (c) 2014 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "xwalk/application/common/manifest_handlers/tizen_application_handler.h" + +#include +#include + +#include "base/strings/utf_string_conversions.h" +#include "base/strings/string_split.h" +#include "third_party/re2/re2/re2.h" +#include "xwalk/application/common/application_manifest_constants.h" + +namespace xwalk { + +namespace keys = application_widget_keys; + +namespace application { + +TizenApplicationInfo::TizenApplicationInfo() { +} + +TizenApplicationInfo::~TizenApplicationInfo() { +} + +TizenApplicationHandler::TizenApplicationHandler() {} + +TizenApplicationHandler::~TizenApplicationHandler() {} + +bool TizenApplicationHandler::Parse(scoped_refptr application, + base::string16* error) { + scoped_ptr app_info(new TizenApplicationInfo); + const Manifest* manifest = application->GetManifest(); + DCHECK(manifest); + + base::Value* app_value = NULL; + manifest->Get(keys::kTizenApplicationKey, &app_value); + // Find an application element with tizen namespace + base::DictionaryValue* app_dict; + std::string value; + bool find = false; + if (app_value && app_value->IsType(base::Value::TYPE_DICTIONARY)) { + app_value->GetAsDictionary(&app_dict); + find = app_dict->GetString(keys::kNamespaceKey, &value); + find = find && (value == kTizenNamespacePrefix); + } else if (app_value && app_value->IsType(base::Value::TYPE_LIST)) { + base::ListValue* list; + app_value->GetAsList(&list); + for (base::ListValue::iterator it = list->begin(); + it != list->end(); ++it) { + (*it)->GetAsDictionary(&app_dict); + find = app_dict->GetString(keys::kNamespaceKey, &value); + find = find && (value == kTizenNamespacePrefix); + if (find) + break; + } + } + + if (!find) { + *error = base::ASCIIToUTF16( + "Cannot find application element with tizen namespace" + " or the tizen namespace prefix is incorrect.\n"); + return false; + } + if (app_dict->GetString(keys::kTizenApplicationIdKey, &value)) + app_info->set_id(value); + if (app_dict->GetString(keys::kTizenApplicationPackageKey, &value)) + app_info->set_package(value); + if (app_dict->GetString(keys::kTizenApplicationRequiredVersionKey, &value)) + app_info->set_required_version(value); + + application->SetManifestData(keys::kTizenApplicationKey, + app_info.release()); + return true; +} + +bool TizenApplicationHandler::Validate( + scoped_refptr application, + std::string* error, + std::vector* warnings) const { + const TizenApplicationInfo* app_info = + static_cast( + application->GetManifestData(keys::kTizenApplicationKey)); + + const char kIdPattern[] = "\\A[0-9a-zA-Z]{10}[.][0-9a-zA-Z]{1,52}\\z"; + const char kPackagePattern[] = "\\A[0-9a-zA-Z]{10}\\z"; + if (!RE2::PartialMatch(app_info->id(), kIdPattern)) { + *error = std::string("The id property of application element" + " does not match the format\n"); + return false; + } + if (!RE2::PartialMatch(app_info->package(), kPackagePattern)) { + *error = std::string("The package property of application element" + " does not match the format\n"); + return false; + } + if (app_info->id().find(app_info->package()) != 0) { + *error = std::string("The application element property id" + " does not start with package.\n"); + return false; + } + // TODO(hongzhang): We need a version map (Tizen API version + // to Crosswalk API version) for checking required_version + if (app_info->required_version().empty()) { + *error = std::string("The required_version property of application" + " element does not exist.\n"); + return false; + } + + return true; +} + +std::vector TizenApplicationHandler::Keys() const { + return std::vector(1, keys::kTizenApplicationKey); +} + +} // namespace application +} // namespace xwalk diff --git a/src/xwalk/application/common/manifest_handlers/tizen_application_handler.h b/src/xwalk/application/common/manifest_handlers/tizen_application_handler.h new file mode 100644 index 0000000..ef45c23 --- /dev/null +++ b/src/xwalk/application/common/manifest_handlers/tizen_application_handler.h @@ -0,0 +1,68 @@ +// Copyright (c) 2014 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#ifndef XWALK_APPLICATION_COMMON_MANIFEST_HANDLERS_TIZEN_APPLICATION_HANDLER_H_ +#define XWALK_APPLICATION_COMMON_MANIFEST_HANDLERS_TIZEN_APPLICATION_HANDLER_H_ + +#include +#include +#include + +#include "base/values.h" +#include "xwalk/application/common/application_data.h" +#include "xwalk/application/common/manifest_handler.h" + +namespace xwalk { +namespace application { + +class TizenApplicationInfo : public ApplicationData::ManifestData { + public: + TizenApplicationInfo(); + virtual ~TizenApplicationInfo(); + + void set_id(const std::string& id) { + id_ = id; + } + void set_package(const std::string& package) { + package_ = package; + } + void set_required_version( + const std::string& required_version) { + required_version_ = required_version; + } + const std::string& id() const { + return id_; + } + const std::string& package() const { + return package_; + } + const std::string& required_version() const { + return required_version_; + } + + private: + std::string id_; + std::string package_; + std::string required_version_; +}; + +class TizenApplicationHandler : public ManifestHandler { + public: + TizenApplicationHandler(); + virtual ~TizenApplicationHandler(); + + virtual bool Parse(scoped_refptr application, + base::string16* error) OVERRIDE; + virtual bool Validate(scoped_refptr application, + std::string* error, + std::vector* warnings) const OVERRIDE; + virtual std::vector Keys() const OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(TizenApplicationHandler); +}; + +} // namespace application +} // namespace xwalk + +#endif // XWALK_APPLICATION_COMMON_MANIFEST_HANDLERS_TIZEN_APPLICATION_HANDLER_H_ diff --git a/src/xwalk/application/common/manifest_handlers/widget_handler.cc b/src/xwalk/application/common/manifest_handlers/widget_handler.cc index dd3cfc7..ee49846 100644 --- a/src/xwalk/application/common/manifest_handlers/widget_handler.cc +++ b/src/xwalk/application/common/manifest_handlers/widget_handler.cc @@ -71,8 +71,9 @@ void ParsePreferenceItem(const base::DictionaryValue* in_value, if (in_value->GetString(keys::kPreferencesValueKey, &pref_value)) out_value->SetString(kPreferencesValue, pref_value); - if (in_value->GetString(keys::kPreferencesReadonlyKey, &pref_readonly)) - out_value->SetString(kPreferencesReadonly, pref_readonly); + if (in_value->GetString(keys::kPreferencesReadonlyKey, &pref_readonly)) { + out_value->SetBoolean(kPreferencesReadonly, pref_readonly == "true"); + } } } // namespace diff --git a/src/xwalk/application/common/manifest_handlers/widget_handler_unittest.cc b/src/xwalk/application/common/manifest_handlers/widget_handler_unittest.cc new file mode 100644 index 0000000..6fecbdd --- /dev/null +++ b/src/xwalk/application/common/manifest_handlers/widget_handler_unittest.cc @@ -0,0 +1,219 @@ +// Copyright (c) 2014 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "xwalk/application/common/manifest_handlers/widget_handler.h" + +#include "xwalk/application/common/application_manifest_constants.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace xwalk { + +namespace keys = application_widget_keys; + +namespace application { + +namespace { +// Below key names are readable from Javascript widget interface. +const char kWidgetAuthor[] = "author"; +const char kWidgetDecription[] = "description"; +const char kWidgetName[] = "name"; +const char kWidgetShortName[] = "shortName"; +const char kWidgetVersion[] = "version"; +const char kWidgetID[] = "id"; +const char kWidgetAuthorEmail[] = "authorEmail"; +const char kWidgetAuthorHref[] = "authorHref"; +const char kWidgetHeight[] = "height"; +const char kWidgetWidth[] = "width"; +const char kWidgetPreferences[] = "preferences"; + +// Child keys inside 'preferences' key. +const char kWidgetPreferencesName[] = "name"; +const char kWidgetPreferencesValue[] = "value"; +const char kWidgetPreferencesReadonly[] = "readonly"; + +// Value to test +const char author[] = "Some one"; +const char decription[] = "This is a test"; +const char name[] = "widget handler unittest"; +const char shortName[] = "whu"; +const char version[] = "0.0.0.1"; +const char ID[] = "iiiiiiiiiid"; +const char authorEmail[] = "aaa@bbb.com"; +const char authorHref[] = "http://www.ssss.com"; +const char height[] = "800"; +const char width[] = "480"; + +const char* preferencesName[] = {"pname0", "pname1", "pname2"}; +const char* preferencesValue[] = {"pvalue0", "pvalue1", "pvalue2"}; +const char* preferencesReadonly[] = {"true", "false", "false"}; +} // namespace + +class WidgetHandlerTest: public testing::Test { + public: + scoped_refptr CreateApplication( + base::DictionaryValue &manifest) { + std::string error; + scoped_refptr application = ApplicationData::Create( + base::FilePath(), Manifest::INVALID_TYPE, manifest, "", &error); + return application; + } + + WidgetInfo* GetWidgetInfo(scoped_refptr application) { + WidgetInfo* info = static_cast( + application->GetManifestData(keys::kWidgetKey)); + return info; + } + + base::DictionaryValue* GetPreferencesItem(int id, + bool is_parsed_manifest_key) { + base::DictionaryValue* preferences = new base::DictionaryValue; + if (is_parsed_manifest_key) { + preferences->SetString(keys::kPreferencesNameKey, + preferencesName[id]); + preferences->SetString(keys::kPreferencesValueKey, + preferencesValue[id]); + // PreferencesReadonly is string on manifest and bool on widgetInfo + preferences->SetString(keys::kPreferencesReadonlyKey, + preferencesReadonly[id]); + } else { + preferences->SetString(kWidgetPreferencesName, + preferencesName[id]); + preferences->SetString(kWidgetPreferencesValue, + preferencesValue[id]); + preferences->SetBoolean(kWidgetPreferencesReadonly, + strncmp(preferencesReadonly[id], "true", 4) == 0); + } + return preferences; + } + + // No Preferences and full other information + void SetAllInfoToManifest(base::DictionaryValue* manifest) { + // Insert some key-value pairs into manifest use full key + manifest->SetString(keys::kAuthorKey, author); + manifest->SetString(keys::kDescriptionKey, decription); + manifest->SetString(keys::kNameKey, name); + manifest->SetString(keys::kShortNameKey, shortName); + manifest->SetString(keys::kVersionKey, version); + manifest->SetString(keys::kIDKey, ID); + manifest->SetString(keys::kAuthorEmailKey, authorEmail); + manifest->SetString(keys::kAuthorHrefKey, authorHref); + manifest->SetString(keys::kHeightKey, height); + manifest->SetString(keys::kWidthKey, width); + } + + // No Preferences and full other information + void SetAllInfoToWidget(base::DictionaryValue* widget) { + // Insert some key-value pairs into widget use widget key; + widget->SetString(kWidgetAuthor, author); + widget->SetString(kWidgetDecription, decription); + widget->SetString(kWidgetName, name); + widget->SetString(kWidgetShortName, shortName); + widget->SetString(kWidgetVersion, version); + widget->SetString(kWidgetID, ID); + widget->SetString(kWidgetAuthorEmail, authorEmail); + widget->SetString(kWidgetAuthorHref, authorHref); + widget->SetString(kWidgetHeight, height); + widget->SetString(kWidgetWidth, width); + } +}; + +TEST_F(WidgetHandlerTest, ParseManifestWithOnlyNameAndVersion) { + base::DictionaryValue manifest; + manifest.SetString(keys::kNameKey, "no name"); + manifest.SetString(keys::kVersionKey, "0"); + + scoped_refptr application = CreateApplication(manifest); + EXPECT_TRUE(application); + + WidgetInfo* info = GetWidgetInfo(application); + int size = info->GetWidgetInfo()->size(); + + // Only name and version ,others are empty string "",but exist. + // And widget have 10 items. + EXPECT_EQ(size, 10); + + base::DictionaryValue* widget = info->GetWidgetInfo(); + base::DictionaryValue::Iterator it(*widget); + + std::string tmpStr; + // Check value + while (!it.IsAtEnd()) { + it.value().GetAsString(&tmpStr); + if (it.key() == kWidgetName) { + EXPECT_EQ(tmpStr, "no name"); + } else if (it.key() == kWidgetVersion) { + EXPECT_EQ(tmpStr, "0"); + } else { + EXPECT_EQ(tmpStr, ""); + } + it.Advance(); + } +} + +TEST_F(WidgetHandlerTest, + ParseManifestWithAllOfOtherItemsAndOnePreferenceItem) { + // Create a manifest with one preference item. + scoped_ptr manifest(new base::DictionaryValue); + SetAllInfoToManifest(manifest.get()); + manifest->Set(keys::kPreferencesKey, GetPreferencesItem(0, true)); + // Create an application use this manifest. + scoped_refptr application; + application = CreateApplication(*(manifest.get())); + EXPECT_TRUE(application); + EXPECT_EQ(application->GetPackageType(), Manifest::TYPE_WGT); + // Get widget info from this application. + WidgetInfo* info = GetWidgetInfo(application); + EXPECT_TRUE(info); + scoped_ptr Copy(info->GetWidgetInfo()->DeepCopy()); + base::DictionaryValue* widget_parsed_from_manifest; + Copy->GetAsDictionary(&widget_parsed_from_manifest); + EXPECT_TRUE(widget_parsed_from_manifest); + + // Create a widget with one preference item manually. + scoped_ptr widget(new base::DictionaryValue); + SetAllInfoToWidget(widget.get()); + widget->Set(kWidgetPreferences, GetPreferencesItem(0, false)); + + // Compare the widget parsed from manifest with + // the widget create manually. + EXPECT_TRUE(widget->Equals(widget_parsed_from_manifest)); +} + +TEST_F(WidgetHandlerTest, + ParseManifestWithAllOfOtherItemsAndThreePreferenceItemsList) { + // Create a manifest with three preference items. + scoped_ptr manifest(new base::DictionaryValue); + SetAllInfoToManifest(manifest.get()); + base::ListValue* manifestPreferences = new base::ListValue; + for (int i = 0; i < 3; i++) { + manifestPreferences->Append(GetPreferencesItem(i, true)); + } + // Create an application use this manifest, + scoped_refptr application; + application = CreateApplication(*(manifest.get())); + EXPECT_TRUE(application); + EXPECT_EQ(application->GetPackageType(), Manifest::TYPE_WGT); + // Get widget info from this application. + WidgetInfo* info = GetWidgetInfo(application); + EXPECT_TRUE(info); + scoped_ptr Copy(info->GetWidgetInfo()->DeepCopy()); + base::DictionaryValue* widget_parsed_from_manifest; + Copy->GetAsDictionary(&widget_parsed_from_manifest); + EXPECT_TRUE(widget_parsed_from_manifest); + + // Create a widget with three preference items manually. + scoped_ptr widget(new base::DictionaryValue); + SetAllInfoToWidget(widget.get()); + base::ListValue* widgetPreferences = new base::ListValue; + for (int i = 0; i < 3; i++) { + widgetPreferences->Append(GetPreferencesItem(i, false)); + } + + // Compare the widget parsed from manifest with + // the widget create manually. + EXPECT_TRUE(widget->Equals(widget_parsed_from_manifest)); +} + +} // namespace application +} // namespace xwalk diff --git a/src/xwalk/application/extension/application_widget_api.js b/src/xwalk/application/extension/application_widget_api.js index 1675c9a..bbc9933 100644 --- a/src/xwalk/application/extension/application_widget_api.js +++ b/src/xwalk/application/extension/application_widget_api.js @@ -3,9 +3,11 @@ // found in the LICENSE file. var application = requireNative('application'); +var common = requireNative('widget_common'); var empty = ""; var zero = 0; + var widgetStringInfo = { "author" : empty, "description" : empty, @@ -29,7 +31,8 @@ function defineReadOnlyProperty(object, key, value) { } else if (key == "height") { value = window.innerHeight; } else if (value == empty) { - value = extension.internal.sendSyncMessage(key); + value = extension.internal.sendSyncMessage( + { cmd: 'GetWidgetInfo', widgetKey: key }); } return value; @@ -41,3 +44,94 @@ for (var key in widgetStringInfo) { defineReadOnlyProperty(exports, key, widgetStringInfo[key]); } +var WidgetStorage = function() { + var _keyList = new Array(); + + this.init = function() { + var result = extension.internal.sendSyncMessage( + { cmd: 'GetAllItems' }); + for (var itemKey in result) { + var itemValue = result[itemKey]; + this[itemKey] = itemValue; + _keyList.push(itemKey); + } + } + + this.__defineGetter__('length', function() { + return _keyList.length; + }); + + this.key = function(index) { + return _keyList[index]; + } + + this.getItem = function(itemKey) { + return this[String(itemKey)]; + } + + this.setItem = function(itemKey, itemValue) { + var result = extension.internal.sendSyncMessage({ + cmd: 'SetPreferencesItem', + preferencesItemKey: String(itemKey), + preferencesItemValue: String(itemValue) }); + + if (result) { + this[String(itemKey)] = String(itemValue); + _keyList.push(String(itemKey)); + } else { + throw new common.CustomDOMException( + common.CustomDOMException.NO_MODIFICATION_ALLOWED_ERR, + 'The object can not be modified.'); + } + }; + + this.removeItem = function(itemKey) { + var result = extension.internal.sendSyncMessage({ + cmd: 'RemovePreferencesItem', + preferencesItemKey: String(itemKey) }); + + if (result) { + delete this[itemKey]; + delete _keyList[_keyList.indexOf(String(itemKey))]; + } else { + throw new common.CustomDOMException( + common.CustomDOMException.NO_MODIFICATION_ALLOWED_ERR, + 'The object can not be modified.'); + } + } + + this.clear = function() { + var itemKey; + var result = extension.internal.sendSyncMessage({ + cmd: 'ClearAllItems' }); + + if (!result) + return; + + for (var i = _keyList.length-1; i >= 0; --i) { + // if the itemKey is still in DB(e.g. readonly), + // we should keep it in JS side. + var exists = extension.internal.sendSyncMessage({ + cmd: 'KeyExists', + preferencesItemKey: _keyList[i] }); + + if (!exists) { + delete this[_keyList[i]]; + _keyList.splice(i, 1); + } + } + } + + this.init(); +}; + +var widgetStorage = new WidgetStorage(); +exports.preferences = widgetStorage; + +Object.defineProperty(exports, 'preferences', { + configurable: false, + enumerable: false, + get: function() { + return widgetStorage; + } +}); diff --git a/src/xwalk/application/extension/application_widget_common_api.js b/src/xwalk/application/extension/application_widget_common_api.js new file mode 100644 index 0000000..60a1599 --- /dev/null +++ b/src/xwalk/application/extension/application_widget_common_api.js @@ -0,0 +1,82 @@ +var errors = { + '1': { type: 'INDEX_SIZE_ERR', name: 'IndexSizeError', message: '' }, + '2': { type: 'DOMSTRING_SIZE_ERR', name: 'DOMStringSizeError', message: '' }, + '3': { type: 'HIERARCHY_REQUEST_ERR', name: 'HierarchyRequestError', message: '' }, + '4': { type: 'WRONG_DOCUMENT_ERR', name: 'WrongDocumentError', message: '' }, + '5': { type: 'INVALID_CHARACTER_ERR', name: 'InvalidCharacterError', message: '' }, + '6': { type: 'NO_DATA_ALLOWED_ERR', name: 'NoDataAllowedError', message: '' }, + '7': { type: 'NO_MODIFICATION_ALLOWED_ERR', name: 'NoModificationAllowedError', message: '' }, + '8': { type: 'NOT_FOUND_ERR', name: 'NotFoundError', message: '' }, + '9': { type: 'NOT_SUPPORTED_ERR', name: 'Not_supportedError', message: '' }, + '10': { type: 'INUSE_ATTRIBUTE_ERR', name: 'InuseAttributeError', message: '' }, + '11': { type: 'INVALID_STATE_ERR', name: 'InvalidStateError', message: '' }, + '12': { type: 'SYNTAX_ERR', name: 'SyntaxError', message: '' }, + '13': { type: 'INVALID_MODIFICATION_ERR', name: 'InvalidModificationError', message: '' }, + '14': { type: 'NAMESPACE_ERR', name: 'NamespaceError', message: '' }, + '15': { type: 'INVALID_ACCESS_ERR', name: 'InvalidAccessError', message: '' }, + '16': { type: 'VALIDATION_ERR', name: 'ValidationError', message: '' }, + '17': { type: 'TYPE_MISMATCH_ERR', name: 'TypeMismatchError', message: '' }, + '18': { type: 'SECURITY_ERR', name: 'SecurityError', message: '' }, + '19': { type: 'NETWORK_ERR', name: 'NetworkError', message: '' }, + '20': { type: 'ABORT_ERR', name: 'AbortError', message: '' }, + '21': { type: 'URL_MISMATCH_ERR', name: 'UrlMismatchError', message: '' }, + '22': { type: 'QUOTA_EXCEEDED_ERR', name: 'QuotaExceededError', message: '' }, + '23': { type: 'TIMEOUT_ERR', name: 'TimeoutError', message: '' }, + '24': { type: 'INVALID_NODE_TYPE_ERR', name: 'InvalidNodeTypeError', message: '' }, + '25': { type: 'DATA_CLONE_ERR', name: 'DataCloneError', message: '' }, +}; + +var CustomDOMException = function(code, message) { + var _code, _message, _name; + + if (typeof code !== 'number') { + throw TypeError('Wrong argument type for Exception.'); + } else if ((code in errors) === false) { + throw TypeError('Unknown exception code: ' + code); + } else { + _code = code; + _name = errors[_code].name; + if (typeof message === 'string') { + _message = message; + } else { + _message = errors[_code].message; + } + } + + var props = {}; + var newException; + + try { + document.removeChild({}) + } catch (e) { + newException = Object.create(e) + } + + var proto = newException.__proto__; + + props = Object.getOwnPropertyDescriptor(proto, "name"); + props.value = _name; + Object.defineProperty(newException, "name", props); + + props = Object.getOwnPropertyDescriptor(proto, "code"); + props.value = _code; + Object.defineProperty(newException, "code", props); + + props = Object.getOwnPropertyDescriptor(proto, "message"); + props.value = _message; + Object.defineProperty(newException, "message", props); + + props.value = function() { + return _name + ": " + _message; + } + Object.defineProperty(newException, "toString", props); + + return newException; +} + +for (var value in errors) { + Object.defineProperty(CustomDOMException, errors[value].type, + { value: parseInt(value) }); +} + +exports.CustomDOMException = CustomDOMException; diff --git a/src/xwalk/application/extension/application_widget_extension.cc b/src/xwalk/application/extension/application_widget_extension.cc index 3b671a3..8db16b7 100644 --- a/src/xwalk/application/extension/application_widget_extension.cc +++ b/src/xwalk/application/extension/application_widget_extension.cc @@ -5,6 +5,8 @@ #include "xwalk/application/extension/application_widget_extension.h" #include "base/bind.h" +#include "base/path_service.h" +#include "base/strings/string_util.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/web_contents.h" #include "ipc/ipc_message.h" @@ -14,10 +16,21 @@ #include "xwalk/application/common/application_data.h" #include "xwalk/application/common/application_manifest_constants.h" #include "xwalk/application/common/manifest_handlers/widget_handler.h" +#include "xwalk/application/extension/application_widget_storage.h" #include "xwalk/runtime/browser/runtime.h" +#include "xwalk/runtime/browser/runtime_context.h" +#include "xwalk/runtime/browser/xwalk_runner.h" +#include "xwalk/runtime/common/xwalk_paths.h" using content::BrowserThread; +namespace { +const char kCommandKey[] = "cmd"; +const char kWidgetAttributeKey[] = "widgetKey"; +const char kPreferencesItemKey[] = "preferencesItemKey"; +const char kPreferencesItemValue[] = "preferencesItemValue"; +} + namespace xwalk { namespace application { @@ -37,36 +50,154 @@ XWalkExtensionInstance* ApplicationWidgetExtension::CreateInstance() { AppWidgetExtensionInstance::AppWidgetExtensionInstance( Application* application) - : application_(application) { + : application_(application), + handler_(this) { DCHECK(application_); + base::ThreadRestrictions::SetIOAllowed(true); + + base::FilePath path; + xwalk::RegisterPathProvider(); + PathService::Get(xwalk::DIR_WGT_STORAGE_PATH, &path); + widget_storage_.reset(new AppWidgetStorage(application_, path)); } +AppWidgetExtensionInstance::~AppWidgetExtensionInstance() {} + void AppWidgetExtensionInstance::HandleMessage(scoped_ptr msg) { + handler_.HandleMessage(msg.Pass()); } void AppWidgetExtensionInstance::HandleSyncMessage( scoped_ptr msg) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - std::string key; - base::Value* result; - base::StringValue* ret_val = base::Value::CreateStringValue(""); + base::DictionaryValue* dict; + std::string command; + msg->GetAsDictionary(&dict); - if (!msg->GetAsString(&key)) { - LOG(ERROR) << "Fail to get sync message as manifest widget key."; - SendSyncReplyToJS(scoped_ptr(ret_val)); + if (!msg->GetAsDictionary(&dict) || !dict->GetString(kCommandKey, &command)) { + LOG(ERROR) << "Fail to handle command sync message."; + SendSyncReplyToJS(scoped_ptr( + base::Value::CreateStringValue(""))); return; } + scoped_ptr result(base::Value::CreateStringValue("")); + if (command == "GetWidgetInfo") { + result = GetWidgetInfo(msg.Pass()); + } else if (command == "SetPreferencesItem") { + result = SetPreferencesItem(msg.Pass()); + } else if (command == "RemovePreferencesItem") { + result = RemovePreferencesItem(msg.Pass()); + } else if (command == "ClearAllItems") { + result = ClearAllItems(msg.Pass()); + } else if (command == "GetAllItems") { + result = GetAllItems(msg.Pass()); + } else if (command == "KeyExists") { + result = KeyExists(msg.Pass()); + } else { + LOG(ERROR) << command << " ASSERT NOT REACHED."; + } + + SendSyncReplyToJS(result.Pass()); +} + +scoped_ptr AppWidgetExtensionInstance::GetWidgetInfo( + scoped_ptr msg) { + scoped_ptr result(base::Value::CreateStringValue("")); + std::string key; + std::string value; + base::DictionaryValue* dict; + + if (!msg->GetAsDictionary(&dict) || + !dict->GetString(kWidgetAttributeKey, &key)) { + LOG(ERROR) << "Fail to get widget attribute key."; + return result.Pass(); + } + WidgetInfo* info = static_cast( application_->data()->GetManifestData(widget_keys::kWidgetKey)); - base::DictionaryValue* value = info->GetWidgetInfo(); - if (!value->Get(key, &result)) { - LOG(ERROR) << "Fail to get value for key: " << key; - SendSyncReplyToJS(scoped_ptr(ret_val)); - } else { - SendSyncReplyToJS(scoped_ptr(result)); + base::DictionaryValue* widget_info = info->GetWidgetInfo(); + widget_info->GetString(key, &value); + result.reset(base::Value::CreateStringValue(value)); + return result.Pass(); +} + +scoped_ptr +AppWidgetExtensionInstance::SetPreferencesItem(scoped_ptr msg) { + scoped_ptr result( + base::Value::CreateBooleanValue(false)); + std::string key; + std::string value; + base::DictionaryValue* dict; + + if (!msg->GetAsDictionary(&dict) || + !dict->GetString(kPreferencesItemKey, &key) || + !dict->GetString(kPreferencesItemValue, &value)) { + LOG(ERROR) << "Fail to set preferences item."; + return result.Pass(); + } + + if (widget_storage_->AddEntry(key, value, false)) + result.reset(base::Value::CreateBooleanValue(true)); + + return result.Pass(); +} + +scoped_ptr +AppWidgetExtensionInstance::RemovePreferencesItem(scoped_ptr msg) { + scoped_ptr result( + base::Value::CreateBooleanValue(false)); + std::string key; + base::DictionaryValue* dict; + + if (!msg->GetAsDictionary(&dict) || + !dict->GetString(kPreferencesItemKey, &key)) { + LOG(ERROR) << "Fail to remove preferences item."; + return result.Pass(); } + + if (widget_storage_->RemoveEntry(key)) + result.reset(base::Value::CreateBooleanValue(true)); + + return result.Pass(); +} + +scoped_ptr AppWidgetExtensionInstance::ClearAllItems( + scoped_ptr msg) { + scoped_ptr result( + base::Value::CreateBooleanValue(false)); + + if (widget_storage_->Clear()) + result.reset(base::Value::CreateBooleanValue(true)); + + return result.Pass(); +} + +scoped_ptr AppWidgetExtensionInstance::GetAllItems( + scoped_ptr msg) { + scoped_ptr result(new base::DictionaryValue()); + widget_storage_->GetAllEntries(result.get()); + + return result.Pass(); +} + +scoped_ptr AppWidgetExtensionInstance::KeyExists( + scoped_ptr msg) const { + scoped_ptr result( + base::Value::CreateBooleanValue(false)); + std::string key; + base::DictionaryValue* dict; + + if (!msg->GetAsDictionary(&dict) || + !dict->GetString(kPreferencesItemKey, &key)) { + LOG(ERROR) << "Fail to remove preferences item."; + return result.Pass(); + } + + if (widget_storage_->EntryExists(key)) + result.reset(base::Value::CreateBooleanValue(true)); + + return result.Pass(); } } // namespace application diff --git a/src/xwalk/application/extension/application_widget_extension.h b/src/xwalk/application/extension/application_widget_extension.h index 25afa99..b37ddc4 100644 --- a/src/xwalk/application/extension/application_widget_extension.h +++ b/src/xwalk/application/extension/application_widget_extension.h @@ -7,6 +7,7 @@ #include +#include "xwalk/extensions/browser/xwalk_extension_function_handler.h" #include "xwalk/extensions/common/xwalk_extension.h" namespace xwalk { @@ -14,6 +15,8 @@ namespace application { class Application; using extensions::XWalkExtension; +using extensions::XWalkExtensionFunctionHandler; +using extensions::XWalkExtensionFunctionInfo; using extensions::XWalkExtensionInstance; class ApplicationWidgetExtension : public XWalkExtension { @@ -30,12 +33,25 @@ class ApplicationWidgetExtension : public XWalkExtension { class AppWidgetExtensionInstance : public XWalkExtensionInstance { public: explicit AppWidgetExtensionInstance(Application* application); + virtual ~AppWidgetExtensionInstance(); virtual void HandleMessage(scoped_ptr msg) OVERRIDE; virtual void HandleSyncMessage(scoped_ptr msg) OVERRIDE; private: + scoped_ptr GetWidgetInfo(scoped_ptr msg); + scoped_ptr SetPreferencesItem( + scoped_ptr mgs); + scoped_ptr RemovePreferencesItem( + scoped_ptr mgs); + scoped_ptr ClearAllItems(scoped_ptr mgs); + scoped_ptr GetAllItems(scoped_ptr mgs); + scoped_ptr KeyExists( + scoped_ptr mgs) const; + Application* application_; + scoped_ptr widget_storage_; + XWalkExtensionFunctionHandler handler_; }; } // namespace application diff --git a/src/xwalk/application/extension/application_widget_storage.cc b/src/xwalk/application/extension/application_widget_storage.cc new file mode 100644 index 0000000..70895a3 --- /dev/null +++ b/src/xwalk/application/extension/application_widget_storage.cc @@ -0,0 +1,306 @@ +// Copyright (c) 2014 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "xwalk/application/extension/application_widget_storage.h" + +#include + +#include "base/file_util.h" +#include "sql/statement.h" +#include "sql/transaction.h" +#include "xwalk/application/common/application_manifest_constants.h" +#include "xwalk/application/common/manifest_handlers/widget_handler.h" + +namespace { + +namespace widget_keys = xwalk::application_widget_keys; + +const char kPreferences[] = "preferences"; +const char kPreferencesName[] = "name"; +const char kPreferencesValue[] = "value"; +const char kPreferencesReadonly[] = "readonly"; + +const base::FilePath::CharType kWidgetStorageExtension[] = + FILE_PATH_LITERAL(".widgetStorage"); + +const char kStorageTableName[] = "widget_storage"; + +const char kCreateStorageTableOp[] = + "CREATE TABLE widget_storage (" + "key TEXT NOT NULL UNIQUE PRIMARY KEY," + "value TEXT NOT NULL," + "read_only INTEGER )"; + +const char kClearStorageTableWithBindOp[] = + "DELETE FROM widget_storage WHERE read_only = ? "; + +const char kInsertItemWithBindOp[] = + "INSERT INTO widget_storage (value, read_only, key) " + "VALUES(?,?,?)"; + +const char kUpdateItemWithBindOp[] = + "UPDATE widget_storage SET value = ? , read_only = ? " + "WHERE key = ?"; + +const char kRemoveItemWithBindOp[] = + "DELETE FROM widget_storage WHERE key = ?"; + +const char kSelectTableLength[] = + "SELECT count(*) FROM widget_storage "; + +const char kSelectCountWithBindOp[] = + "SELECT count(*) FROM widget_storage " + "WHERE key = ?"; + +const char kSelectAllItem[] = + "SELECT key, value FROM widget_storage "; + +const char kSelectReadOnlyWithBindOp[] = + "SELECT read_only FROM widget_storage " + "WHERE key = ?"; +} // namespace + +namespace xwalk { +namespace application { + +AppWidgetStorage::AppWidgetStorage(Application* application, + const base::FilePath& data_dir) + : application_(application), + db_initialized_(false) { + sqlite_db_.reset(new sql::Connection); + + base::FilePath name(application_->id()); + base::FilePath::StringType storage_name = + name.value() + kWidgetStorageExtension; + data_path_ = data_dir.Append(storage_name); + + if (!base::PathExists(data_dir) && !base::CreateDirectory(data_dir)) { + LOG(ERROR) << "Could not create widget storage path."; + return; + } + + if (!Init()) { + LOG(ERROR) << "Initialize widget storage failed."; + return; + } +} + +AppWidgetStorage::~AppWidgetStorage() { +} + +bool AppWidgetStorage::Init() { + if (!sqlite_db_->Open(data_path_)) { + LOG(ERROR) << "Unable to open widget storage DB."; + return false; + } + sqlite_db_->Preload(); + + if (!InitStorageTable()) { + LOG(ERROR) << "Unable to init widget storage table."; + return false; + } + return db_initialized_; +} + +bool AppWidgetStorage::SaveConfigInfoItem(base::DictionaryValue* dict) { + DCHECK(dict); + std::string key; + std::string value; + bool read_only = false; + dict->GetString(kPreferencesName, &key); + dict->GetString(kPreferencesValue, &value); + dict->GetBoolean(kPreferencesReadonly, &read_only); + return AddEntry(key, value, read_only); +} + +bool AppWidgetStorage::SaveConfigInfoInDB() { + WidgetInfo* info = + static_cast( + application_->data()->GetManifestData(widget_keys::kWidgetKey)); + base::DictionaryValue* widget_info = info->GetWidgetInfo(); + if (!widget_info) { + LOG(ERROR) << "Fail to get parsed widget information."; + return false; + } + + base::Value* pref_value; + widget_info->Get(kPreferences, &pref_value); + + if (pref_value && pref_value->IsType(base::Value::TYPE_DICTIONARY)) { + base::DictionaryValue* dict; + pref_value->GetAsDictionary(&dict); + return SaveConfigInfoItem(dict); + } else if (pref_value && pref_value->IsType(base::Value::TYPE_LIST)) { + base::ListValue* list; + pref_value->GetAsList(&list); + + for (base::ListValue::iterator it = list->begin(); + it != list->end(); ++it) { + base::DictionaryValue* dict; + (*it)->GetAsDictionary(&dict); + if (!SaveConfigInfoItem(dict)) + return false; + } + } else { + LOG(INFO) << "No widget preferences or preference type is not supported."; + } + + return true; +} + +bool AppWidgetStorage::InitStorageTable() { + if (sqlite_db_->DoesTableExist(kStorageTableName)) { + db_initialized_ = (sqlite_db_ && sqlite_db_->is_open()); + return true; + } + + sql::Transaction transaction(sqlite_db_.get()); + transaction.Begin(); + if (!sqlite_db_->Execute(kCreateStorageTableOp)) + return false; + if (!transaction.Commit()) + return false; + + db_initialized_ = (sqlite_db_ && sqlite_db_->is_open()); + SaveConfigInfoInDB(); + + return true; +} + +bool AppWidgetStorage::EntryExists(const std::string& key) const { + sql::Transaction transaction(sqlite_db_.get()); + if (!transaction.Begin()) + return false; + + sql::Statement stmt(sqlite_db_->GetUniqueStatement( + kSelectCountWithBindOp)); + stmt.BindString(0, key); + if (!stmt.Step()) { + LOG(ERROR) << "An error occured when selecting count from DB."; + return false; + } + + if (!transaction.Commit()) + return false; + + int exist = stmt.ColumnInt(0); + return exist > 0; +} + +bool AppWidgetStorage::IsReadOnly(const std::string& key) { + sql::Transaction transaction(sqlite_db_.get()); + if (!transaction.Begin()) + return true; + + sql::Statement stmt(sqlite_db_->GetUniqueStatement( + kSelectReadOnlyWithBindOp)); + stmt.BindString(0, key); + if (!stmt.Step()) { + LOG(ERROR) << "An error occured when selecting count from DB."; + return true; + } + + if (!transaction.Commit()) + return true; + + return stmt.ColumnBool(0); +} + +bool AppWidgetStorage::AddEntry(const std::string& key, + const std::string& value, + bool read_only) { + if (!db_initialized_ && !Init()) + return false; + + std::string operation; + if (!EntryExists(key)) { + operation = kInsertItemWithBindOp; + } else if (!IsReadOnly(key)) { + operation = kUpdateItemWithBindOp; + } else { + LOG(ERROR) << "Could not set read only item " << key; + return false; + } + + sql::Transaction transaction(sqlite_db_.get()); + if (!transaction.Begin()) + return false; + + sql::Statement stmt(sqlite_db_->GetUniqueStatement( + operation.c_str())); + stmt.BindString(0, value); + stmt.BindBool(1, read_only); + stmt.BindString(2, key); + if (!stmt.Run()) { + LOG(ERROR) << "An error occured when set item into DB."; + return false; + } + + return transaction.Commit(); +} + +bool AppWidgetStorage::RemoveEntry(const std::string& key) { + if (!db_initialized_ && !Init()) + return false; + + if (IsReadOnly(key)) { + LOG(ERROR) << "Could not remove read only item " << key; + return false; + } + + sql::Transaction transaction(sqlite_db_.get()); + if (!transaction.Begin()) + return false; + + sql::Statement stmt(sqlite_db_->GetUniqueStatement( + kRemoveItemWithBindOp)); + stmt.BindString(0, key); + + if (!stmt.Run()) { + LOG(ERROR) << "An error occured when removing item into DB."; + return false; + } + + return transaction.Commit(); +} + +bool AppWidgetStorage::Clear() { + if (!db_initialized_ && !Init()) + return false; + + sql::Transaction transaction(sqlite_db_.get()); + transaction.Begin(); + + sql::Statement stmt(sqlite_db_->GetUniqueStatement( + kClearStorageTableWithBindOp)); + stmt.BindBool(0, false); + + if (!stmt.Run()) { + LOG(ERROR) << "An error occured when removing item into DB."; + return false; + } + + return transaction.Commit(); +} + +bool AppWidgetStorage::GetAllEntries(base::DictionaryValue* result) { + std::string key; + std::string value; + DCHECK(result); + + if (!db_initialized_ && !Init()) + return false; + + sql::Statement stmt(sqlite_db_->GetUniqueStatement(kSelectAllItem)); + while (stmt.Step()) { + key = stmt.ColumnString(0); + value = stmt.ColumnString(1); + result->SetString(key, value); + } + + return true; +} + +} // namespace application +} // namespace xwalk diff --git a/src/xwalk/application/extension/application_widget_storage.h b/src/xwalk/application/extension/application_widget_storage.h new file mode 100644 index 0000000..ad83ed6 --- /dev/null +++ b/src/xwalk/application/extension/application_widget_storage.h @@ -0,0 +1,50 @@ +// Copyright (c) 2014 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef XWALK_APPLICATION_EXTENSION_APPLICATION_WIDGET_STORAGE_H_ +#define XWALK_APPLICATION_EXTENSION_APPLICATION_WIDGET_STORAGE_H_ + +#include +#include + +#include "base/values.h" +#include "base/files/file_path.h" +#include "sql/connection.h" +#include "xwalk/application/browser/application.h" + +namespace xwalk { +namespace application { + +class AppWidgetStorage { + public: + AppWidgetStorage(Application* application, + const base::FilePath& data_dir); + ~AppWidgetStorage(); + + // Adds or replaces entry (if not readonly); + // returns true on success. + bool AddEntry(const std::string& key, + const std::string& value, + bool read_only); + bool RemoveEntry(const std::string& key); + bool Clear(); + bool GetAllEntries(base::DictionaryValue* result); + bool EntryExists(const std::string& key) const; + + private: + bool Init(); + bool IsReadOnly(const std::string& key); + bool InitStorageTable(); + bool SaveConfigInfoInDB(); + bool SaveConfigInfoItem(base::DictionaryValue* dict); + + Application* application_; + scoped_ptr sqlite_db_; + base::FilePath data_path_; + bool db_initialized_; +}; + +} // namespace application +} // namespace xwalk +#endif // XWALK_APPLICATION_EXTENSION_APPLICATION_WIDGET_STORAGE_H_ diff --git a/src/xwalk/application/tools/linux/xwalk_launcher_main.cc b/src/xwalk/application/tools/linux/xwalk_launcher_main.cc index dd5e09a..af8551f 100644 --- a/src/xwalk/application/tools/linux/xwalk_launcher_main.cc +++ b/src/xwalk/application/tools/linux/xwalk_launcher_main.cc @@ -25,11 +25,26 @@ static const char* xwalk_running_manager_iface = static const char* xwalk_running_app_iface = "org.crosswalkproject.Running.Application1"; -static const char cmd_line_fullscreen_arg[] = "--fullscreen"; - static char* application_object_path; static GMainLoop* mainloop; +static GDBusConnection* g_connection; + +static int g_argc; +static char** g_argv; +static gboolean query_running = FALSE; +static gboolean fullscreen = FALSE; +static gchar** cmd_appid; + +static GOptionEntry entries[] = { + { "running", 'r', 0, G_OPTION_ARG_NONE, &query_running, + "Check whether the application is running", NULL }, + { "fullscreen", 'f', 0, G_OPTION_ARG_NONE, &fullscreen, + "Run the application as fullscreen", NULL }, + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &cmd_appid, + "ID of the application to be launched", NULL }, + { NULL } +}; static void object_removed(GDBusObjectManager* manager, GDBusObject* object, gpointer user_data) { @@ -73,64 +88,52 @@ static void on_app_properties_changed(GDBusProxy* proxy, } } -int main(int argc, char** argv) { - GError* error = NULL; - char* appid; - gboolean fullscreen = FALSE; - - -#if !GLIB_CHECK_VERSION(2, 36, 0) - // g_type_init() is deprecated on GLib since 2.36, Tizen has 2.32. - g_type_init(); -#endif - - if (xwalk_tizen_set_home_for_user_app()) - exit(1); - - if (!strcmp(basename(argv[0]), "xwalk-launcher")) { - if (argc < 2) { - fprintf(stderr, "No AppID informed, nothing to do\n"); - exit(1); - } - - appid = argv[1]; +static void query_application_running(GDBusObjectManager* running_om, + const char* app_id) { + GList* objects = g_dbus_object_manager_get_objects(running_om); + GList* it; + bool is_running = FALSE; + + for (it = objects; it; it = it->next) { + GDBusObject* object = reinterpret_cast(it->data); + GDBusInterface* iface = g_dbus_object_get_interface( + object, + xwalk_running_app_iface); + if (!iface) + continue; - if (argc > 2) { - if (!strcmp(basename(argv[2]), cmd_line_fullscreen_arg)) - fullscreen = TRUE; + GDBusProxy* proxy = G_DBUS_PROXY(iface); + GVariant* id_variant; + id_variant = g_dbus_proxy_get_cached_property(proxy, "AppID"); + if (!id_variant) { + g_object_unref(iface); + continue; } - } else { - appid = strdup(basename(argv[0])); - - if (argc > 1) { - if (!strcmp(basename(argv[1]), cmd_line_fullscreen_arg)) - fullscreen = TRUE; + const gchar* id; + g_variant_get(id_variant, "s", &id); + if (!strcmp(app_id, id)) { + is_running = TRUE; + break; } - } - GDBusConnection* connection = get_session_bus_connection(&error); - if (!connection) { - fprintf(stderr, "Couldn't get the session bus connection: %s\n", - error->message); - exit(1); + g_object_unref(iface); } + const char* str = is_running ? "running" : "not running"; + g_print("Application %s is %s.\n", app_id, str); - GDBusObjectManager* running_apps_om = g_dbus_object_manager_client_new_sync( - connection, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, - xwalk_service_name, xwalk_running_path, - NULL, NULL, NULL, NULL, &error); - if (!running_apps_om) { - fprintf(stderr, "Service '%s' does could not be reached: %s\n", - xwalk_service_name, error->message); - exit(1); - } + g_list_free_full(objects, g_object_unref); +} - g_signal_connect(running_apps_om, "object-removed", +static void launch_application(GDBusObjectManager* running_apps_manager, + const char* appid, + gboolean fullscreen) { + GError* error = NULL; + g_signal_connect(running_apps_manager, "object-removed", G_CALLBACK(object_removed), NULL); GDBusProxy* running_proxy = g_dbus_proxy_new_sync( - connection, + g_connection, G_DBUS_PROXY_FLAGS_NONE, NULL, xwalk_service_name, xwalk_running_path, xwalk_running_manager_iface, NULL, &error); if (!running_proxy) { @@ -142,12 +145,9 @@ int main(int argc, char** argv) { unsigned int launcher_pid = getpid(); - GVariant* result = g_dbus_proxy_call_sync(running_proxy, "Launch", - g_variant_new("(sub)", appid, - launcher_pid, - fullscreen), - G_DBUS_CALL_FLAGS_NONE, - -1, NULL, &error); + GVariant* result = g_dbus_proxy_call_sync(running_proxy, "Launch", + g_variant_new("(sub)", appid, launcher_pid, fullscreen), + G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (!result) { fprintf(stderr, "Couldn't call 'Launch' method: %s\n", error->message); exit(1); @@ -158,7 +158,7 @@ int main(int argc, char** argv) { application_object_path); GDBusProxy* app_proxy = g_dbus_proxy_new_sync( - connection, + g_connection, G_DBUS_PROXY_FLAGS_NONE, NULL, xwalk_service_name, application_object_path, xwalk_running_app_iface, NULL, &error); if (!app_proxy) { @@ -177,13 +177,71 @@ int main(int argc, char** argv) { char name[128]; snprintf(name, sizeof(name), "xwalk-%s", appid); - if (xwalk_appcore_init(argc, argv, name)) { + if (xwalk_appcore_init(g_argc, g_argv, name)) { fprintf(stderr, "Failed to initialize appcore"); exit(1); } #endif g_main_loop_run(mainloop); +} + +int main(int argc, char** argv) { + GError* error = NULL; + char* appid; + + g_argc = argc; + g_argv = argv; + +#if !GLIB_CHECK_VERSION(2, 36, 0) + // g_type_init() is deprecated on GLib since 2.36. + g_type_init(); +#endif + + if (xwalk_tizen_set_home_for_user_app()) + exit(1); + + GOptionContext* context = + g_option_context_new("- Crosswalk Application Launcher"); + g_option_context_add_main_entries(context, entries, NULL); + if (!g_option_context_parse(context, &argc, &argv, &error)) { + fprintf(stderr, "Option parsing failed: %s\n", error->message); + exit(1); + } + + if (!strcmp(basename(argv[0]), "xwalk-launcher")) { + if (cmd_appid == NULL) { + fprintf(stderr, "No AppID informed, nothing to do.\n"); + return 0; + } + appid = cmd_appid[0]; + } else { + appid = strdup(basename(argv[0])); + } + + g_connection = get_session_bus_connection(&error); + if (!g_connection) { + fprintf(stderr, "Couldn't get the session bus connection: %s\n", + error->message); + exit(1); + } + + GDBusObjectManager* running_apps_manager = + g_dbus_object_manager_client_new_sync( + g_connection, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, + xwalk_service_name, xwalk_running_path, + NULL, NULL, NULL, NULL, &error); + if (!running_apps_manager) { + fprintf(stderr, "Service '%s' does could not be reached: %s\n", + xwalk_service_name, error->message); + exit(1); + } + + if (query_running) { + query_application_running(running_apps_manager, appid); + } else { + launch_application(running_apps_manager, appid, fullscreen); + } return 0; } diff --git a/src/xwalk/application/xwalk_application.gypi b/src/xwalk/application/xwalk_application.gypi index 9be9a99..ae83293 100644 --- a/src/xwalk/application/xwalk_application.gypi +++ b/src/xwalk/application/xwalk_application.gypi @@ -83,6 +83,8 @@ 'extension/application_runtime_extension.h', 'extension/application_widget_extension.cc', 'extension/application_widget_extension.h', + 'extension/application_widget_storage.cc', + 'extension/application_widget_storage.h', 'renderer/application_native_module.cc', 'renderer/application_native_module.h', @@ -113,12 +115,17 @@ 'dependencies': [ 'build/system.gyp:tizen', 'tizen/xwalk_tizen.gypi:xwalk_tizen_lib', + '../third_party/re2/re2.gyp:re2', ], 'sources': [ 'browser/installer/tizen/packageinfo_constants.cc', 'browser/installer/tizen/packageinfo_constants.h', 'browser/installer/tizen/service_package_installer.cc', 'browser/installer/tizen/service_package_installer.h', + 'common/manifest_handlers/navigation_handler.cc', + 'common/manifest_handlers/navigation_handler.h', + 'common/manifest_handlers/tizen_application_handler.cc', + 'common/manifest_handlers/tizen_application_handler.h', ], }], ], diff --git a/src/xwalk/build/common.gypi b/src/xwalk/build/common.gypi index 27acabe..b2ddb98 100644 --- a/src/xwalk/build/common.gypi +++ b/src/xwalk/build/common.gypi @@ -1,15 +1,5 @@ { 'variables': { - # Enable multitouch support with XInput2.1. When it is enabled, unlike - # XInput2.2, there are no XI_TouchBegin, XI_TouchUpdate and XI_TouchEnd - # raw touch events emitted from touch device. Instead, the touch event is - # simulated by a normal mouse event, since X server maintains multiple - # virtual touch screen devices as floating device, each of them can - # simulate a touch event tracked by the device id of event source, as a - # result, multi-touch support works with these simulated touch events - # dispatched from floating device. - 'enable_xi21_mt%': 0, - 'tizen%': 0, 'tizen_mobile%': 0, }, @@ -19,9 +9,6 @@ 'tizen_mobile%': '<(tizen_mobile)', }, 'conditions': [ - ['enable_xi21_mt==1', { - 'defines': ['ENABLE_XI21_MT=1'], - }], ['tizen==1', { 'defines': ['OS_TIZEN=1'], }], diff --git a/src/xwalk/gyp_xwalk b/src/xwalk/gyp_xwalk index 1401f37..d816137 100755 --- a/src/xwalk/gyp_xwalk +++ b/src/xwalk/gyp_xwalk @@ -41,12 +41,6 @@ sys.path.insert(1, os.path.join(chrome_src, 'third_party', 'liblouis')) sys.path.insert(1, os.path.join(chrome_src, 'third_party', 'WebKit', 'Source', 'build', 'scripts')) -# FIXME(rakuco,joone): This is a workaround to allow the Tizen build to -# proceed, as we use make there and GN isn't involved in this case (otherwise -# we run into problems like GN always looking for GTK+ dependencies). -if os.environ.get('GYP_GENERATORS') != 'make': - import find_depot_tools - # On Windows, Psyco shortens warm runs of build/gyp_chromium by about # 20 seconds on a z600 machine with 12 GB of RAM, from 90 down to 70 # seconds. Conversely, memory usage of build/gyp_chromium with Psyco @@ -373,6 +367,7 @@ if __name__ == '__main__': # TODO(bradnelson): take this out once this issue is fixed: # http://code.google.com/p/gyp/issues/detail?id=177 if sys.platform == 'cygwin': + import find_depot_tools depot_tools_path = find_depot_tools.add_depot_tools_to_path() python_dir = sorted(glob.glob(os.path.join(depot_tools_path, 'python2*_bin')))[-1] @@ -436,6 +431,7 @@ if __name__ == '__main__': # by depot_tools, then use it. if (sys.platform in ('win32', 'cygwin') and os.environ.get('GYP_GENERATORS') == 'ninja'): + import find_depot_tools depot_tools_path = find_depot_tools.add_depot_tools_to_path() toolchain = os.path.normpath(os.path.join( depot_tools_path, 'win_toolchain', 'vs2013_files')) @@ -465,13 +461,6 @@ if __name__ == '__main__': supplemental_includes = GetSupplementalFiles() - # FIXME(rakuco,joone): This is a workaround to allow the Tizen build to - # proceed, as we use make there and GN isn't involved in this case (otherwise - # we run into problems like GN always looking for GTK+ dependencies). - if os.environ.get('GYP_GENERATORS') != 'make': - if not RunGN(supplemental_includes): - sys.exit(1) - args.extend( ['-I' + i for i in additional_include_files(supplemental_includes, args)]) diff --git a/src/xwalk/packaging/crosswalk-2.31-enable-VAVDA-with-EGL.patch b/src/xwalk/packaging/crosswalk-2.31-enable-VAVDA-with-EGL.patch deleted file mode 100644 index 6595561..0000000 --- a/src/xwalk/packaging/crosswalk-2.31-enable-VAVDA-with-EGL.patch +++ /dev/null @@ -1,1313 +0,0 @@ -From c9f2fa16578bc20c83247e72608b5d6ca4dff6ba Mon Sep 17 00:00:00 2001 -From: "qing.zhang" -Date: Thu, 7 Nov 2013 08:59:38 -0500 -Subject: [PATCH] [Tizen] Enabling Hardware Acceleration with Libva and EGL in - VDA for Tizen Mobile within chromium v31+. - -Why we need to maintain it in our side: -=========================================== -1) Upstream confirm VAVDA will continue to be restricted to - CrOS/X86 for dev & testing only and not for chromium road map. -2) CrOS/X86 no plan to expend EGL backend which finalize in - June 2012 and be addressed to the CrOS graphics team. - -So, the upstream no plan to lerage VAVDA with EGL graphic - backend for any X86. - -3) The tizen-mobile's driver only support EGL as texture - backend. The video hw acceleration of xwalk have to - rely on EGL not GLX to bind decoded pixmap. -=========================================== -That's why we enable specific EGL for VAVDA in tizen port. ---- - .../gpu/media/gpu_video_decode_accelerator.cc | 8 + - .../media/vaapi_video_decode_accelerator_tizen.cc | 908 +++++++++++++++++++++ - .../media/vaapi_video_decode_accelerator_tizen.h | 273 +++++++ - content/content_common.gypi | 26 + - 4 files changed, 1215 insertions(+) - create mode 100644 content/common/gpu/media/vaapi_video_decode_accelerator_tizen.cc - create mode 100644 content/common/gpu/media/vaapi_video_decode_accelerator_tizen.h - -diff --git a/content/common/gpu/media/gpu_video_decode_accelerator.cc b/content/common/gpu/media/gpu_video_decode_accelerator.cc -index bd1dc5f..c5a6df2 100644 ---- a/content/common/gpu/media/gpu_video_decode_accelerator.cc -+++ b/content/common/gpu/media/gpu_video_decode_accelerator.cc -@@ -32,6 +32,8 @@ - #include "content/common/gpu/media/vaapi_video_decode_accelerator.h" - #include "ui/gl/gl_context_glx.h" - #include "ui/gl/gl_implementation.h" -+#elif defined(OS_TIZEN_MOBILE) && defined(ARCH_CPU_X86_FAMILY) -+#include "content/common/gpu/media/vaapi_video_decode_accelerator_tizen.h" - #elif defined(OS_ANDROID) - #include "content/common/gpu/media/android_video_decode_accelerator.h" - #endif -@@ -296,6 +298,12 @@ void GpuVideoDecodeAccelerator::Initialize( - static_cast(stub_->decoder()->GetGLContext()); - video_decode_accelerator_.reset(new VaapiVideoDecodeAccelerator( - glx_context->display(), this, make_context_current_)); -+#elif defined(OS_TIZEN_MOBILE) && defined(ARCH_CPU_X86_FAMILY) -+ video_decode_accelerator_.reset(new VaapiVideoDecodeAccelerator( -+ gfx::GLSurfaceEGL::GetHardwareDisplay(), -+ stub_->decoder()->GetGLContext()->GetHandle(), -+ this, -+ make_context_current_)); - #elif defined(OS_ANDROID) - video_decode_accelerator_.reset(new AndroidVideoDecodeAccelerator( - this, -diff --git a/content/common/gpu/media/vaapi_video_decode_accelerator_tizen.cc b/content/common/gpu/media/vaapi_video_decode_accelerator_tizen.cc -new file mode 100644 -index 0000000..cfff457 ---- /dev/null -+++ b/content/common/gpu/media/vaapi_video_decode_accelerator_tizen.cc -@@ -0,0 +1,908 @@ -+// Copyright (c) 2013 Intel Corporation. All rights reserved. -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+ -+#include "base/bind.h" -+#include "base/debug/trace_event.h" -+#include "base/logging.h" -+#include "base/metrics/histogram.h" -+#include "base/stl_util.h" -+#include "base/strings/string_util.h" -+#include "base/synchronization/waitable_event.h" -+#include "content/child/child_thread.h" -+#include "content/common/gpu/gpu_channel.h" -+#include "content/common/gpu/media/vaapi_video_decode_accelerator_tizen.h" -+#include "media/base/bind_to_current_loop.h" -+#include "media/video/picture.h" -+#include "ui/gl/gl_bindings.h" -+#include "ui/gl/scoped_binders.h" -+ -+static void ReportToUMA( -+ content::VaapiH264Decoder::VAVDAH264DecoderFailure failure) { -+ UMA_HISTOGRAM_ENUMERATION( -+ "Media.VAVDAH264.DecoderFailure", -+ failure, -+ content::VaapiH264Decoder::VAVDA_H264_DECODER_FAILURES_MAX); -+} -+ -+namespace content { -+ -+#define RETURN_AND_NOTIFY_ON_FAILURE(result, log, error_code, ret) \ -+ do { \ -+ if (!(result)) { \ -+ DVLOG(1) << log; \ -+ NotifyError(error_code); \ -+ return ret; \ -+ } \ -+ } while (0) -+ -+VaapiVideoDecodeAccelerator::InputBuffer::InputBuffer() : id(0), size(0) { -+} -+ -+VaapiVideoDecodeAccelerator::InputBuffer::~InputBuffer() { -+} -+ -+void VaapiVideoDecodeAccelerator::NotifyError(Error error) { -+ if (message_loop_ != base::MessageLoop::current()) { -+ DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current()); -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::NotifyError, weak_this_, error)); -+ return; -+ } -+ -+ // Post Cleanup() as a task so we don't recursively acquire lock_. -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::Cleanup, weak_this_)); -+ -+ DVLOG(1) << "Notifying of error " << error; -+ if (client_) { -+ client_->NotifyError(error); -+ client_ptr_factory_.InvalidateWeakPtrs(); -+ } -+} -+ -+// TFPPicture allocates X Pixmaps and binds them to textures passed -+// in PictureBuffers from clients to them. TFPPictures are created as -+// a consequence of receiving a set of PictureBuffers from clients and released -+// at the end of decode (or when a new set of PictureBuffers is required). -+// -+// TFPPictures are used for output, contents of VASurfaces passed from decoder -+// are put into the associated pixmap memory and sent to client. -+class VaapiVideoDecodeAccelerator::TFPPicture { -+ public: -+ ~TFPPicture(); -+ -+ static linked_ptr Create( -+ const base::Callback& make_context_current, -+ EGLDisplay egl_display, -+ Display* x_display, -+ int32 picture_buffer_id, -+ uint32 texture_id, -+ gfx::Size size); -+ int32 picture_buffer_id() { -+ return picture_buffer_id_; -+ } -+ -+ uint32 texture_id() { -+ return texture_id_; -+ } -+ -+ gfx::Size size() { -+ return size_; -+ } -+ -+ int x_pixmap() { -+ return x_pixmap_; -+ } -+ -+ // Bind texture to pixmap. Needs to be called every frame. -+ bool Bind(); -+ -+ private: -+ TFPPicture(const base::Callback& make_context_current, -+ Display* x_display, -+ int32 picture_buffer_id, -+ uint32 texture_id, -+ gfx::Size size); -+ -+ bool Initialize(EGLDisplay egl_display); -+ -+ base::Callback make_context_current_; -+ -+ Display* x_display_; -+ -+ // Output id for the client. -+ int32 picture_buffer_id_; -+ uint32 texture_id_; -+ -+ gfx::Size size_; -+ -+ // Pixmaps bound to this texture. -+ Pixmap x_pixmap_; -+ EGLDisplay egl_display_; -+ EGLImageKHR egl_image_; -+ -+ DISALLOW_COPY_AND_ASSIGN(TFPPicture); -+}; -+ -+VaapiVideoDecodeAccelerator::TFPPicture::TFPPicture( -+ const base::Callback& make_context_current, -+ Display* x_display, -+ int32 picture_buffer_id, -+ uint32 texture_id, -+ gfx::Size size) -+ : make_context_current_(make_context_current), -+ x_display_(x_display), -+ picture_buffer_id_(picture_buffer_id), -+ texture_id_(texture_id), -+ size_(size), -+ x_pixmap_(0), -+ egl_image_(0) { -+ DCHECK(!make_context_current_.is_null()); -+}; -+ -+linked_ptr -+VaapiVideoDecodeAccelerator::TFPPicture::Create( -+ const base::Callback& make_context_current, -+ EGLDisplay egl_display, -+ Display* x_display, -+ int32 picture_buffer_id, -+ uint32 texture_id, -+ gfx::Size size) { -+ -+ linked_ptr tfp_picture( -+ new TFPPicture(make_context_current, x_display, -+ picture_buffer_id, texture_id, size)); -+ -+ if (!tfp_picture->Initialize(egl_display)) -+ tfp_picture.reset(); -+ -+ return tfp_picture; -+} -+ -+bool VaapiVideoDecodeAccelerator::TFPPicture::Initialize( -+ EGLDisplay egl_display) { -+ // Check for NULL prevents unittests from crashing on nonexistent ChildThread. -+ DCHECK(ChildThread::current() == NULL || -+ ChildThread::current()->message_loop() == base::MessageLoop::current()); -+ -+ if (!make_context_current_.Run()) -+ return false; -+ -+ XWindowAttributes win_attr; -+ int screen = DefaultScreen(x_display_); -+ XGetWindowAttributes(x_display_, RootWindow(x_display_, screen), &win_attr); -+ //TODO(posciak): pass the depth required by libva, not the RootWindow's depth -+ x_pixmap_ = XCreatePixmap(x_display_, RootWindow(x_display_, screen), -+ size_.width(), size_.height(), win_attr.depth); -+ if (!x_pixmap_) { -+ DVLOG(1) << "Failed creating an X Pixmap for TFP"; -+ return false; -+ } -+ -+ egl_display_ = egl_display; -+ EGLint image_attrs[] = { EGL_IMAGE_PRESERVED_KHR, 1 , EGL_NONE }; -+ -+ egl_image_ = eglCreateImageKHR(egl_display_, -+ EGL_NO_CONTEXT, -+ EGL_NATIVE_PIXMAP_KHR, -+ (EGLClientBuffer)x_pixmap_, -+ image_attrs); -+ if (!egl_image_) { -+ DVLOG(1) << "Failed creating a EGLImage from Pixmap for KHR"; -+ return false; -+ } -+ -+ return true; -+} -+VaapiVideoDecodeAccelerator::TFPPicture::~TFPPicture() { -+ // Check for NULL prevents unittests from crashing on non-existing ChildThread. -+ DCHECK(ChildThread::current() == NULL || -+ ChildThread::current()->message_loop() == base::MessageLoop::current()); -+ -+ // Unbind surface from texture and deallocate resources. -+ if (make_context_current_.Run()) { -+ eglDestroyImageKHR(egl_display_, egl_image_); -+ } -+ -+ if (x_pixmap_) -+ XFreePixmap(x_display_, x_pixmap_); -+ XSync(x_display_, False); // Needed to work around buggy vdpau-driver. -+} -+ -+bool VaapiVideoDecodeAccelerator::TFPPicture::Bind() { -+ DCHECK(x_pixmap_); -+ DCHECK(egl_image_); -+ -+ // Check for NULL prevents unittests from crashing on nonexistent ChildThread. -+ DCHECK(ChildThread::current() == NULL || -+ ChildThread::current()->message_loop() == base::MessageLoop::current()); -+ -+ if (!make_context_current_.Run()) -+ return false; -+ -+ gfx::ScopedTextureBinder texture_binder(GL_TEXTURE_2D, texture_id_); -+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_); -+ -+ return true; -+} -+ -+VaapiVideoDecodeAccelerator::TFPPicture* -+ VaapiVideoDecodeAccelerator::TFPPictureById(int32 picture_buffer_id) { -+ TFPPictures::iterator it = tfp_pictures_.find(picture_buffer_id); -+ if (it == tfp_pictures_.end()) { -+ DVLOG(1) << "Picture id " << picture_buffer_id << " does not exist"; -+ return NULL; -+ } -+ -+ return it->second.get(); -+} -+ -+VaapiVideoDecodeAccelerator::VaapiVideoDecodeAccelerator( -+ EGLDisplay egl_display, EGLContext egl_context, -+ Client* client, -+ const base::Callback& make_context_current) -+ : x_display_(0), -+ egl_display_(egl_display), -+ egl_context_(egl_context), -+ make_context_current_(make_context_current), -+ state_(kUninitialized), -+ input_ready_(&lock_), -+ surfaces_available_(&lock_), -+ message_loop_(base::MessageLoop::current()), -+ weak_this_(base::AsWeakPtr(this)), -+ client_ptr_factory_(client), -+ client_(client_ptr_factory_.GetWeakPtr()), -+ decoder_thread_("VaapiDecoderThread"), -+ num_frames_at_client_(0), -+ num_stream_bufs_at_decoder_(0), -+ finish_flush_pending_(false), -+ awaiting_va_surfaces_recycle_(false), -+ requested_num_pics_(0) { -+ DCHECK(client); -+} -+VaapiVideoDecodeAccelerator::~VaapiVideoDecodeAccelerator() { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+} -+ -+class ScopedPtrXFree { -+ public: -+ void operator()(void* x) const { -+ ::XFree(x); -+ } -+}; -+ -+bool VaapiVideoDecodeAccelerator::Initialize( -+ media::VideoCodecProfile profile) { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ -+ base::AutoLock auto_lock(lock_); -+ DCHECK_EQ(state_, kUninitialized); -+ DVLOG(2) << "Initializing VAVDA, profile: " << profile; -+ -+ if (!make_context_current_.Run()) -+ return false; -+ -+ x_display_ = base::MessagePumpForUI::GetDefaultXDisplay(); -+ -+ vaapi_wrapper_ = VaapiWrapper::Create( -+ profile, x_display_, -+ base::Bind(&ReportToUMA, content::VaapiH264Decoder::VAAPI_ERROR)); -+ -+ if (!vaapi_wrapper_.get()) { -+ DVLOG(1) << "Failed initializing VAAPI"; -+ return false; -+ } -+ -+ decoder_.reset( -+ new VaapiH264Decoder( -+ vaapi_wrapper_.get(), -+ media::BindToCurrentLoop(base::Bind( -+ &VaapiVideoDecodeAccelerator::SurfaceReady, weak_this_)), -+ base::Bind(&ReportToUMA))); -+ -+ CHECK(decoder_thread_.Start()); -+ -+ state_ = kIdle; -+ -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &Client::NotifyInitializeDone, client_)); -+ return true; -+} -+ -+void VaapiVideoDecodeAccelerator::SurfaceReady( -+ int32 input_id, -+ const scoped_refptr& va_surface) { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ DCHECK(!awaiting_va_surfaces_recycle_); -+ -+ // Drop any requests to output if we are resetting or being destroyed. -+ if (state_ == kResetting || state_ == kDestroying) -+ return; -+ -+ pending_output_cbs_.push( -+ base::Bind(&VaapiVideoDecodeAccelerator::OutputPicture, -+ weak_this_, va_surface, input_id)); -+ -+ TryOutputSurface(); -+} -+ -+void VaapiVideoDecodeAccelerator::OutputPicture( -+ const scoped_refptr& va_surface, -+ int32 input_id, -+ TFPPicture* tfp_picture) { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ -+ int32 output_id = tfp_picture->picture_buffer_id(); -+ -+ TRACE_EVENT2("Video Decoder", "VAVDA::OutputSurface", -+ "input_id", input_id, -+ "output_id", output_id); -+ -+ DVLOG(3) << "Outputting VASurface " << va_surface->id() -+ << " into pixmap bound to picture buffer id " << output_id; -+ -+ RETURN_AND_NOTIFY_ON_FAILURE(tfp_picture->Bind(), -+ "Failed binding texture to pixmap", -+ PLATFORM_FAILURE, ); -+ -+ RETURN_AND_NOTIFY_ON_FAILURE( -+ vaapi_wrapper_->PutSurfaceIntoPixmap(va_surface->id(), -+ tfp_picture->x_pixmap(), -+ tfp_picture->size()), -+ "Failed putting surface into pixmap", PLATFORM_FAILURE, ); -+ -+ // Notify the client a picture is ready to be displayed. -+ ++num_frames_at_client_; -+ TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_); -+ DVLOG(4) << "Notifying output picture id " << output_id -+ << " for input "<< input_id << " is ready"; -+ client_->PictureReady(media::Picture(output_id, input_id)); -+} -+ -+void VaapiVideoDecodeAccelerator::TryOutputSurface() { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ -+ // Handle Destroy() arriving while pictures are queued for output. -+ if (!client_) -+ return; -+ -+ if (pending_output_cbs_.empty() || output_buffers_.empty()) -+ return; -+ -+ OutputCB output_cb = pending_output_cbs_.front(); -+ pending_output_cbs_.pop(); -+ -+ TFPPicture* tfp_picture = TFPPictureById(output_buffers_.front()); -+ DCHECK(tfp_picture); -+ output_buffers_.pop(); -+ -+ output_cb.Run(tfp_picture); -+ -+ if (finish_flush_pending_ && pending_output_cbs_.empty()) -+ FinishFlush(); -+} -+ -+void VaapiVideoDecodeAccelerator::MapAndQueueNewInputBuffer( -+ const media::BitstreamBuffer& bitstream_buffer) { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ TRACE_EVENT1("Video Decoder", "MapAndQueueNewInputBuffer", "input_id", -+ bitstream_buffer.id()); -+ -+ DVLOG(4) << "Mapping new input buffer id: " << bitstream_buffer.id() -+ << " size: " << (int)bitstream_buffer.size(); -+ -+ scoped_ptr shm( -+ new base::SharedMemory(bitstream_buffer.handle(), true)); -+ RETURN_AND_NOTIFY_ON_FAILURE(shm->Map(bitstream_buffer.size()), -+ "Failed to map input buffer", UNREADABLE_INPUT,); -+ -+ base::AutoLock auto_lock(lock_); -+ -+ // Set up a new input buffer and queue it for later. -+ linked_ptr input_buffer(new InputBuffer()); -+ input_buffer->shm.reset(shm.release()); -+ input_buffer->id = bitstream_buffer.id(); -+ input_buffer->size = bitstream_buffer.size(); -+ -+ ++num_stream_bufs_at_decoder_; -+ TRACE_COUNTER1("Video Decoder", "Stream buffers at decoder", -+ num_stream_bufs_at_decoder_); -+ -+ input_buffers_.push(input_buffer); -+ input_ready_.Signal(); -+} -+ -+bool VaapiVideoDecodeAccelerator::GetInputBuffer_Locked() { -+ DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current()); -+ lock_.AssertAcquired(); -+ -+ if (curr_input_buffer_.get()) -+ return true; -+ -+ // Will only wait if it is expected that in current state new buffers will -+ // be queued from the client via Decode(). The state can change during wait. -+ while (input_buffers_.empty() && (state_ == kDecoding || state_ == kIdle)) { -+ input_ready_.Wait(); -+ } -+ -+ // We could have got woken up in a different state or never got to sleep -+ // due to current state; check for that. -+ switch (state_) { -+ case kFlushing: -+ // Here we are only interested in finishing up decoding buffers that are -+ // already queued up. Otherwise will stop decoding. -+ if (input_buffers_.empty()) -+ return false; -+ // else fallthrough -+ case kDecoding: -+ case kIdle: -+ DCHECK(!input_buffers_.empty()); -+ -+ curr_input_buffer_ = input_buffers_.front(); -+ input_buffers_.pop(); -+ -+ DVLOG(4) << "New current bitstream buffer, id: " -+ << curr_input_buffer_->id -+ << " size: " << curr_input_buffer_->size; -+ -+ decoder_->SetStream( -+ static_cast(curr_input_buffer_->shm->memory()), -+ curr_input_buffer_->size, curr_input_buffer_->id); -+ return true; -+ -+ default: -+ // We got woken up due to being destroyed/reset, ignore any already -+ // queued inputs. -+ return false; -+ } -+} -+ -+void VaapiVideoDecodeAccelerator::ReturnCurrInputBuffer_Locked() { -+ lock_.AssertAcquired(); -+ DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current()); -+ DCHECK(curr_input_buffer_.get()); -+ -+ int32 id = curr_input_buffer_->id; -+ curr_input_buffer_.reset(); -+ DVLOG(4) << "End of input buffer " << id; -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &Client::NotifyEndOfBitstreamBuffer, client_, id)); -+ -+ --num_stream_bufs_at_decoder_; -+ TRACE_COUNTER1("Video Decoder", "Stream buffers at decoder", -+ num_stream_bufs_at_decoder_); -+} -+ -+bool VaapiVideoDecodeAccelerator::FeedDecoderWithOutputSurfaces_Locked() { -+ lock_.AssertAcquired(); -+ DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current()); -+ -+ while (available_va_surfaces_.empty() && -+ (state_ == kDecoding || state_ == kFlushing || state_ == kIdle)) { -+ surfaces_available_.Wait(); -+ } -+ -+ if (state_ != kDecoding && state_ != kFlushing && state_ != kIdle) -+ return false; -+ -+ VASurface::ReleaseCB va_surface_release_cb = -+ media::BindToCurrentLoop(base::Bind( -+ &VaapiVideoDecodeAccelerator::RecycleVASurfaceID, weak_this_)); -+ -+ while (!available_va_surfaces_.empty()) { -+ scoped_refptr va_surface( -+ new VASurface(available_va_surfaces_.front(), va_surface_release_cb)); -+ available_va_surfaces_.pop_front(); -+ decoder_->ReuseSurface(va_surface); -+ } -+ -+ return true; -+} -+ -+void VaapiVideoDecodeAccelerator::DecodeTask() { -+ DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current()); -+ TRACE_EVENT0("Video Decoder", "VAVDA::DecodeTask"); -+ base::AutoLock auto_lock(lock_); -+ -+ if (state_ != kDecoding) -+ return; -+ -+ // Main decode task. -+ DVLOG(4) << "Decode task"; -+ -+ // Try to decode what stream data is (still) in the decoder until we run out -+ // of it. -+ while (GetInputBuffer_Locked()) { -+ DCHECK(curr_input_buffer_.get()); -+ -+ VaapiH264Decoder::DecResult res; -+ { -+ // We are OK releasing the lock here, as decoder never calls our methods -+ // directly and we will reacquire the lock before looking at state again. -+ // This is the main decode function of the decoder and while keeping -+ // the lock for its duration would be fine, it would defeat the purpose -+ // of having a separate decoder thread. -+ base::AutoUnlock auto_unlock(lock_); -+ res = decoder_->Decode(); -+ } -+ -+ switch (res) { -+ case VaapiH264Decoder::kAllocateNewSurfaces: -+ DVLOG(1) << "Decoder requesting a new set of surfaces"; -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange, weak_this_, -+ decoder_->GetRequiredNumOfPictures(), -+ decoder_->GetPicSize())); -+ // We'll get rescheduled once ProvidePictureBuffers() finishes. -+ return; -+ -+ case VaapiH264Decoder::kRanOutOfStreamData: -+ ReturnCurrInputBuffer_Locked(); -+ break; -+ -+ case VaapiH264Decoder::kRanOutOfSurfaces: -+ // No more output buffers in the decoder, try getting more or go to -+ // sleep waiting for them. -+ if (!FeedDecoderWithOutputSurfaces_Locked()) -+ return; -+ -+ break; -+ -+ case VaapiH264Decoder::kDecodeError: -+ RETURN_AND_NOTIFY_ON_FAILURE(false, "Error decoding stream", -+ PLATFORM_FAILURE, ); -+ return; -+ } -+ } -+} -+ -+void VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange(size_t num_pics, -+ gfx::Size size) { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ DCHECK(!awaiting_va_surfaces_recycle_); -+ -+ // At this point decoder has stopped running and has already posted onto our -+ // loop any remaining output request callbacks, which executed before we got -+ // here. Some of them might have been pended though, because we might not -+ // have had enough TFPictures to output surfaces to. Initiate a wait cycle, -+ // which will wait for client to return enough PictureBuffers to us, so that -+ // we can finish all pending output callbacks, releasing associated surfaces. -+ DVLOG(1) << "Initiating surface set change"; -+ awaiting_va_surfaces_recycle_ = true; -+ -+ requested_num_pics_ = num_pics; -+ requested_pic_size_ = size; -+ -+ TryFinishSurfaceSetChange(); -+} -+ -+void VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange() { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ -+ if (!awaiting_va_surfaces_recycle_) -+ return; -+ -+ if (!pending_output_cbs_.empty() || -+ tfp_pictures_.size() != available_va_surfaces_.size()) { -+ // Either: -+ // 1. Not all pending pending output callbacks have been executed yet. -+ // Wait for the client to return enough pictures and retry later. -+ // 2. The above happened and all surface release callbacks have been posted -+ // as the result, but not all have executed yet. Post ourselves after them -+ // to let them release surfaces. -+ DVLOG(2) << "Awaiting pending output/surface release callbacks to finish"; -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange, weak_this_)); -+ return; -+ } -+ -+ // All surfaces released, destroy them and dismiss all PictureBuffers. -+ awaiting_va_surfaces_recycle_ = false; -+ available_va_surfaces_.clear(); -+ vaapi_wrapper_->DestroySurfaces(); -+ -+ for (TFPPictures::iterator iter = tfp_pictures_.begin(); -+ iter != tfp_pictures_.end(); ++iter) { -+ DVLOG(2) << "Dismissing picture id: " << iter->first; -+ client_->DismissPictureBuffer(iter->first); -+ } -+ tfp_pictures_.clear(); -+ -+ // And ask for a new set as requested. -+ DVLOG(1) << "Requesting " << requested_num_pics_ << " pictures of size: " -+ << requested_pic_size_.ToString(); -+ -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &Client::ProvidePictureBuffers, client_, -+ requested_num_pics_, requested_pic_size_, GL_TEXTURE_2D)); -+} -+ -+void VaapiVideoDecodeAccelerator::Decode( -+ const media::BitstreamBuffer& bitstream_buffer) { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ -+ TRACE_EVENT1("Video Decoder", "VAVDA::Decode", "Buffer id", -+ bitstream_buffer.id()); -+ -+ // We got a new input buffer from the client, map it and queue for later use. -+ MapAndQueueNewInputBuffer(bitstream_buffer); -+ -+ base::AutoLock auto_lock(lock_); -+ switch (state_) { -+ case kIdle: -+ state_ = kDecoding; -+ decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::DecodeTask, -+ base::Unretained(this))); -+ break; -+ -+ case kDecoding: -+ // Decoder already running. -+ case kResetting: -+ // When resetting, allow accumulating bitstream buffers, so that -+ // the client can queue after-seek-buffers while we are finishing with -+ // the before-seek one. -+ break; -+ -+ default: -+ RETURN_AND_NOTIFY_ON_FAILURE(false, -+ "Decode request from client in invalid state: " << state_, -+ PLATFORM_FAILURE, ); -+ break; -+ } -+} -+ -+void VaapiVideoDecodeAccelerator::RecycleVASurfaceID( -+ VASurfaceID va_surface_id) { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ base::AutoLock auto_lock(lock_); -+ -+ available_va_surfaces_.push_back(va_surface_id); -+ surfaces_available_.Signal(); -+} -+ -+void VaapiVideoDecodeAccelerator::AssignPictureBuffers( -+ const std::vector& buffers) { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ -+ base::AutoLock auto_lock(lock_); -+ DCHECK(tfp_pictures_.empty()); -+ -+ while (!output_buffers_.empty()) -+ output_buffers_.pop(); -+ -+ RETURN_AND_NOTIFY_ON_FAILURE( -+ buffers.size() == requested_num_pics_, -+ "Got an invalid number of picture buffers. (Got " << buffers.size() -+ << ", requested " << requested_num_pics_ << ")", INVALID_ARGUMENT, ); -+ DCHECK(requested_pic_size_ == buffers[0].size()); -+ -+ std::vector va_surface_ids; -+ RETURN_AND_NOTIFY_ON_FAILURE( -+ vaapi_wrapper_->CreateSurfaces(requested_pic_size_, -+ buffers.size(), -+ &va_surface_ids), -+ "Failed creating VA Surfaces", PLATFORM_FAILURE, ); -+ DCHECK_EQ(va_surface_ids.size(), buffers.size()); -+ -+ for (size_t i = 0; i < buffers.size(); ++i) { -+ DVLOG(2) << "Assigning picture id: " << buffers[i].id() -+ << " to texture id: " << buffers[i].texture_id() -+ << " VASurfaceID: " << va_surface_ids[i]; -+ -+ linked_ptr tfp_picture( -+ TFPPicture::Create(make_context_current_, egl_display_, x_display_, -+ buffers[i].id(), buffers[i].texture_id(), -+ requested_pic_size_)); -+ -+ RETURN_AND_NOTIFY_ON_FAILURE( -+ tfp_picture.get(), "Failed assigning picture buffer to a texture.", -+ PLATFORM_FAILURE, ); -+ -+ bool inserted = tfp_pictures_.insert(std::make_pair( -+ buffers[i].id(), tfp_picture)).second; -+ DCHECK(inserted); -+ -+ output_buffers_.push(buffers[i].id()); -+ available_va_surfaces_.push_back(va_surface_ids[i]); -+ surfaces_available_.Signal(); -+ } -+ -+ state_ = kDecoding; -+ decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::DecodeTask, base::Unretained(this))); -+} -+ -+void VaapiVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id) { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ TRACE_EVENT1("Video Decoder", "VAVDA::ReusePictureBuffer", "Picture id", -+ picture_buffer_id); -+ -+ --num_frames_at_client_; -+ TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_); -+ -+ output_buffers_.push(picture_buffer_id); -+ TryOutputSurface(); -+} -+ -+void VaapiVideoDecodeAccelerator::FlushTask() { -+ DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current()); -+ DVLOG(1) << "Flush task"; -+ -+ // First flush all the pictures that haven't been outputted, notifying the -+ // client to output them. -+ bool res = decoder_->Flush(); -+ RETURN_AND_NOTIFY_ON_FAILURE(res, "Failed flushing the decoder.", -+ PLATFORM_FAILURE, ); -+ -+ // Put the decoder in idle state, ready to resume. -+ decoder_->Reset(); -+ -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::FinishFlush, weak_this_)); -+} -+ -+void VaapiVideoDecodeAccelerator::Flush() { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ DVLOG(1) << "Got flush request"; -+ -+ base::AutoLock auto_lock(lock_); -+ state_ = kFlushing; -+ // Queue a flush task after all existing decoding tasks to clean up. -+ decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::FlushTask, base::Unretained(this))); -+ -+ input_ready_.Signal(); -+ surfaces_available_.Signal(); -+} -+ -+void VaapiVideoDecodeAccelerator::FinishFlush() { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ -+ finish_flush_pending_ = false; -+ -+ base::AutoLock auto_lock(lock_); -+ if (state_ != kFlushing) { -+ DCHECK_EQ(state_, kDestroying); -+ return; // We could've gotten destroyed already. -+ } -+ -+ // Still waiting for textures from client to finish outputting all pending -+ // frames. Try again later. -+ if (!pending_output_cbs_.empty()) { -+ finish_flush_pending_ = true; -+ return; -+ } -+ -+ state_ = kIdle; -+ -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &Client::NotifyFlushDone, client_)); -+ -+ DVLOG(1) << "Flush finished"; -+} -+ -+void VaapiVideoDecodeAccelerator::ResetTask() { -+ DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current()); -+ DVLOG(1) << "ResetTask"; -+ -+ // All the decoding tasks from before the reset request from client are done -+ // by now, as this task was scheduled after them and client is expected not -+ // to call Decode() after Reset() and before NotifyResetDone. -+ decoder_->Reset(); -+ -+ base::AutoLock auto_lock(lock_); -+ -+ // Return current input buffer, if present. -+ if (curr_input_buffer_.get()) -+ ReturnCurrInputBuffer_Locked(); -+ -+ // And let client know that we are done with reset. -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::FinishReset, weak_this_)); -+} -+ -+void VaapiVideoDecodeAccelerator::Reset() { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ DVLOG(1) << "Got reset request"; -+ -+ // This will make any new decode tasks exit early. -+ base::AutoLock auto_lock(lock_); -+ state_ = kResetting; -+ finish_flush_pending_ = false; -+ -+ // Drop all remaining input buffers, if present. -+ while (!input_buffers_.empty()) { -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &Client::NotifyEndOfBitstreamBuffer, client_, -+ input_buffers_.front()->id)); -+ input_buffers_.pop(); -+ } -+ -+ decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::ResetTask, base::Unretained(this))); -+ -+ input_ready_.Signal(); -+ surfaces_available_.Signal(); -+} -+ -+void VaapiVideoDecodeAccelerator::FinishReset() { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ DVLOG(1) << "FinishReset"; -+ base::AutoLock auto_lock(lock_); -+ -+ if (state_ != kResetting) { -+ DCHECK(state_ == kDestroying || state_ == kUninitialized) << state_; -+ return; // We could've gotten destroyed already. -+ } -+ -+ // Drop pending outputs. -+ while (!pending_output_cbs_.empty()) -+ pending_output_cbs_.pop(); -+ -+ if (awaiting_va_surfaces_recycle_) { -+ // Decoder requested a new surface set while we were waiting for it to -+ // finish the last DecodeTask, running at the time of Reset(). -+ // Let the surface set change finish first before resetting. -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::FinishReset, weak_this_)); -+ return; -+ } -+ -+ num_stream_bufs_at_decoder_ = 0; -+ state_ = kIdle; -+ -+ message_loop_->PostTask(FROM_HERE, base::Bind( -+ &Client::NotifyResetDone, client_)); -+ -+ // The client might have given us new buffers via Decode() while we were -+ // resetting and might be waiting for our move, and not call Decode() anymore -+ // until we return something. Post a DecodeTask() so that we won't -+ // sleep forever waiting for Decode() in that case. Having two of them -+ // in the pipe is harmless, the additional one will return as soon as it sees -+ // that we are back in kDecoding state. -+ if (!input_buffers_.empty()) { -+ state_ = kDecoding; -+ decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( -+ &VaapiVideoDecodeAccelerator::DecodeTask, -+ base::Unretained(this))); -+ } -+ -+ DVLOG(1) << "Reset finished"; -+} -+ -+void VaapiVideoDecodeAccelerator::Cleanup() { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ -+ if (state_ == kUninitialized || state_ == kDestroying) -+ return; -+ -+ DVLOG(1) << "Destroying VAVDA"; -+ base::AutoLock auto_lock(lock_); -+ state_ = kDestroying; -+ -+ client_ptr_factory_.InvalidateWeakPtrs(); -+ -+ { -+ base::AutoUnlock auto_unlock(lock_); -+ // Post a dummy task to the decoder_thread_ to ensure it is drained. -+ base::WaitableEvent waiter(false, false); -+ decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( -+ &base::WaitableEvent::Signal, base::Unretained(&waiter))); -+ input_ready_.Signal(); -+ surfaces_available_.Signal(); -+ waiter.Wait(); -+ decoder_thread_.Stop(); -+ } -+ -+ state_ = kUninitialized; -+} -+ -+void VaapiVideoDecodeAccelerator::Destroy() { -+ DCHECK_EQ(message_loop_, base::MessageLoop::current()); -+ Cleanup(); -+ delete this; -+} -+ -+} // namespace content -diff --git a/content/common/gpu/media/vaapi_video_decode_accelerator_tizen.h b/content/common/gpu/media/vaapi_video_decode_accelerator_tizen.h -new file mode 100644 -index 0000000..d41cf38 ---- /dev/null -+++ b/content/common/gpu/media/vaapi_video_decode_accelerator_tizen.h -@@ -0,0 +1,273 @@ -+// Copyright (c) 2013 Intel Corporation. All rights reserved. -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+// -+// This file contains an implementation of VideoDecoderAccelerator -+// that utilizes hardware video decoder present on Intel CPUs for Tizen. -+ -+#ifndef CONTENT_COMMON_GPU_MEDIA_VAAPI_VIDEO_DECODE_ACCELERATOR_H_ -+#define CONTENT_COMMON_GPU_MEDIA_VAAPI_VIDEO_DECODE_ACCELERATOR_H_ -+ -+#include -+#include -+#include -+#include -+ -+#include "base/logging.h" -+#include "base/memory/linked_ptr.h" -+#include "base/memory/shared_memory.h" -+#include "base/memory/weak_ptr.h" -+#include "base/message_loop/message_loop.h" -+#include "base/synchronization/condition_variable.h" -+#include "base/synchronization/lock.h" -+#include "base/threading/non_thread_safe.h" -+#include "base/threading/thread.h" -+#include "content/common/content_export.h" -+#include "content/common/gpu/media/vaapi_h264_decoder.h" -+#include "content/common/gpu/media/vaapi_wrapper.h" -+#include "content/common/gpu/media/video_decode_accelerator_impl.h" -+#include "media/base/bitstream_buffer.h" -+#include "media/video/picture.h" -+#include "media/video/video_decode_accelerator.h" -+#include "ui/gl/gl_bindings.h" -+ -+namespace content { -+ -+// Class to provide video decode acceleration for Intel systems with hardware -+// support for it, and on which libva is available. -+// Decoding tasks are performed in a separate decoding thread. -+// -+// Threading/life-cycle: this object is created & destroyed on the GPU -+// ChildThread. A few methods on it are called on the decoder thread which is -+// stopped during |this->Destroy()|, so any tasks posted to the decoder thread -+// can assume |*this| is still alive. See |weak_this_| below for more details. -+class CONTENT_EXPORT VaapiVideoDecodeAccelerator -+ : public VideoDecodeAcceleratorImpl { -+ public: -+ VaapiVideoDecodeAccelerator( -+ EGLDisplay egl_display, EGLContext egl_context, -+ Client* client, -+ const base::Callback& make_context_current); -+ virtual ~VaapiVideoDecodeAccelerator(); -+ -+ // media::VideoDecodeAccelerator implementation. -+ virtual bool Initialize(media::VideoCodecProfile profile) OVERRIDE; -+ virtual void Decode(const media::BitstreamBuffer& bitstream_buffer) OVERRIDE; -+ virtual void AssignPictureBuffers( -+ const std::vector& buffers) OVERRIDE; -+ virtual void ReusePictureBuffer(int32 picture_buffer_id) OVERRIDE; -+ virtual void Flush() OVERRIDE; -+ virtual void Reset() OVERRIDE; -+ virtual void Destroy() OVERRIDE; -+ -+private: -+ // Notify the client that |output_id| is ready for displaying. -+ void NotifyPictureReady(int32 input_id, int32 output_id); -+ -+ // Notify the client that an error has occurred and decoding cannot continue. -+ void NotifyError(Error error); -+ -+ // Map the received input buffer into this process' address space and -+ // queue it for decode. -+ void MapAndQueueNewInputBuffer( -+ const media::BitstreamBuffer& bitstream_buffer); -+ -+ // Get a new input buffer from the queue and set it up in decoder. This will -+ // sleep if no input buffers are available. Return true if a new buffer has -+ // been set up, false if an early exit has been requested (due to initiated -+ // reset/flush/destroy). -+ bool GetInputBuffer_Locked(); -+ -+ // Signal the client that the current buffer has been read and can be -+ // returned. Will also release the mapping. -+ void ReturnCurrInputBuffer_Locked(); -+ -+ // Pass one or more output buffers to the decoder. This will sleep -+ // if no buffers are available. Return true if buffers have been set up or -+ // false if an early exit has been requested (due to initiated -+ // reset/flush/destroy). -+ bool FeedDecoderWithOutputSurfaces_Locked(); -+ -+ // Continue decoding given input buffers and sleep waiting for input/output -+ // as needed. Will exit if a new set of surfaces or reset/flush/destroy -+ // is requested. -+ void DecodeTask(); -+ -+ // Scheduled after receiving a flush request and executed after the current -+ // decoding task finishes decoding pending inputs. Makes the decoder return -+ // all remaining output pictures and puts it in an idle state, ready -+ // to resume if needed and schedules a FinishFlush. -+ void FlushTask(); -+ -+ // Scheduled by the FlushTask after decoder is flushed to put VAVDA into idle -+ // state and notify the client that flushing has been finished. -+ void FinishFlush(); -+ -+ // Scheduled after receiving a reset request and executed after the current -+ // decoding task finishes decoding the current frame. Puts the decoder into -+ // an idle state, ready to resume if needed, discarding decoded but not yet -+ // outputted pictures (decoder keeps ownership of their associated picture -+ // buffers). Schedules a FinishReset afterwards. -+ void ResetTask(); -+ -+ // Scheduled by ResetTask after it's done putting VAVDA into an idle state. -+ // Drops remaining input buffers and notifies the client that reset has been -+ // finished. -+ void FinishReset(); -+ -+ // Helper for Destroy(), doing all the actual work except for deleting self. -+ void Cleanup(); -+ -+ // Get a usable framebuffer configuration for use in binding textures -+ // or return false on failure. -+ bool InitializeFBConfig(); -+ -+ // Callback for the decoder to execute when it wants us to output given -+ // |va_surface|. -+ void SurfaceReady(int32 input_id, const scoped_refptr& va_surface); -+ -+ // Represents a texture bound to an X Pixmap for output purposes. -+ class TFPPicture; -+ -+ // Callback to be executed once we have a |va_surface| to be output and -+ // an available |tfp_picture| to use for output. -+ // Puts contents of |va_surface| into given |tfp_picture|, releases the -+ // surface and passes the resulting picture to client for output. -+ void OutputPicture(const scoped_refptr& va_surface, -+ int32 input_id, -+ TFPPicture* tfp_picture); -+ -+ // Try to OutputPicture() if we have both a ready surface and picture. -+ void TryOutputSurface(); -+ -+ // Called when a VASurface is no longer in use by the decoder or is not being -+ // synced/waiting to be synced to a picture. Returns it to available surfaces -+ // pool. -+ void RecycleVASurfaceID(VASurfaceID va_surface_id); -+ -+ // Initiate wait cycle for surfaces to be released before we release them -+ // and allocate new ones, as requested by the decoder. -+ void InitiateSurfaceSetChange(size_t num_pics, gfx::Size size); -+ // Check if the surfaces have been released or post ourselves for later. -+ void TryFinishSurfaceSetChange(); -+ -+ // Client-provided X/EGL state. -+ Display* x_display_; -+ EGLDisplay egl_display_; -+ EGLContext egl_context_; -+ base::Callback make_context_current_; -+ -+ // VAVDA state. -+ enum State { -+ // Initialize() not called yet or failed. -+ kUninitialized, -+ // DecodeTask running. -+ kDecoding, -+ // Resetting, waiting for decoder to finish current task and cleanup. -+ kResetting, -+ // Flushing, waiting for decoder to finish current task and cleanup. -+ kFlushing, -+ // Idle, decoder in state ready to start/resume decoding. -+ kIdle, -+ // Destroying, waiting for the decoder to finish current task. -+ kDestroying, -+ }; -+ -+ // Protects input buffer and surface queues and state_. -+ base::Lock lock_; -+ State state_; -+ -+ // An input buffer awaiting consumption, provided by the client. -+ struct InputBuffer { -+ InputBuffer(); -+ ~InputBuffer(); -+ -+ int32 id; -+ size_t size; -+ scoped_ptr shm; -+ }; -+ -+ // Queue for incoming input buffers. -+ typedef std::queue > InputBuffers; -+ InputBuffers input_buffers_; -+ // Signalled when input buffers are queued onto the input_buffers_ queue. -+ base::ConditionVariable input_ready_; -+ -+ // Current input buffer at decoder. -+ linked_ptr curr_input_buffer_; -+ -+ // Queue for incoming output buffers (texture ids). -+ typedef std::queue OutputBuffers; -+ OutputBuffers output_buffers_; -+ -+ typedef std::map > TFPPictures; -+ // All allocated TFPPictures, regardless of their current state. TFPPictures -+ // are allocated once and destroyed at the end of decode. -+ TFPPictures tfp_pictures_; -+ -+ // Return a TFPPicture associated with given client-provided id. -+ TFPPicture* TFPPictureById(int32 picture_buffer_id); -+ -+ // VA Surfaces no longer in use that can be passed back to the decoder for -+ // reuse, once it requests them. -+ std::list available_va_surfaces_; -+ // Signalled when output surfaces are queued onto the available_va_surfaces_ -+ // queue. -+ base::ConditionVariable surfaces_available_; -+ -+ // Pending output requests from the decoder. When it indicates that we should -+ // output a surface and we have an available TFPPicture (i.e. texture) ready -+ // to use, we'll execute the callback passing the TFPPicture. The callback -+ // will put the contents of the surface into the picture and return it to -+ // the client, releasing the surface as well. -+ // If we don't have any available TFPPictures at the time when the decoder -+ // requests output, we'll store the request on pending_output_cbs_ queue for -+ // later and run it once the client gives us more textures -+ // via ReusePictureBuffer(). -+ typedef base::Callback OutputCB; -+ std::queue pending_output_cbs_; -+ -+ // ChildThread's message loop -+ base::MessageLoop* message_loop_; -+ -+ // WeakPtr<> pointing to |this| for use in posting tasks from the decoder -+ // thread back to the ChildThread. Because the decoder thread is a member of -+ // this class, any task running on the decoder thread is guaranteed that this -+ // object is still alive. As a result, tasks posted from ChildThread to -+ // decoder thread should use base::Unretained(this), and tasks posted from the -+ // decoder thread to the ChildThread should use |weak_this_|. -+ base::WeakPtr weak_this_; -+ -+ // To expose client callbacks from VideoDecodeAccelerator. -+ // NOTE: all calls to these objects *MUST* be executed on message_loop_. -+ base::WeakPtrFactory client_ptr_factory_; -+ base::WeakPtr client_; -+ -+ scoped_ptr vaapi_wrapper_; -+ -+ // Comes after vaapi_wrapper_ to ensure its destructor is executed before -+ // vaapi_wrapper_ is destroyed. -+ scoped_ptr decoder_; -+ base::Thread decoder_thread_; -+ -+ int num_frames_at_client_; -+ int num_stream_bufs_at_decoder_; -+ -+ // Whether we are waiting for any pending_output_cbs_ to be run before -+ // NotifyingFlushDone. -+ bool finish_flush_pending_; -+ -+ // Decoder requested a new surface set and we are waiting for all the surfaces -+ // to be returned before we can free them. -+ bool awaiting_va_surfaces_recycle_; -+ -+ // Last requested number/resolution of output picture buffers. -+ size_t requested_num_pics_; -+ gfx::Size requested_pic_size_; -+ -+ DISALLOW_COPY_AND_ASSIGN(VaapiVideoDecodeAccelerator); -+}; -+ -+} // namespace content -+ -+#endif // CONTENT_COMMON_GPU_MEDIA_VAAPI_VIDEO_DECODE_ACCELERATOR_H_ -diff --git a/content/content_common.gypi b/content/content_common.gypi -index 9d6cb61..3f53dd5 100644 ---- a/content/content_common.gypi -+++ b/content/content_common.gypi -@@ -583,6 +583,32 @@ - '<(DEPTH)/third_party/libva', - ], - }], -+ ['target_arch != "arm" and tizen_mobile == 1 and use_x11 == 1', { -+ 'dependencies': [ -+ '../media/media.gyp:media', -+ ], -+ 'sources': [ -+ 'common/gpu/media/h264_dpb.cc', -+ 'common/gpu/media/h264_dpb.h', -+ 'common/gpu/media/va_surface.h', -+ 'common/gpu/media/vaapi_h264_decoder.cc', -+ 'common/gpu/media/vaapi_h264_decoder.h', -+ 'common/gpu/media/vaapi_video_decode_accelerator_tizen.cc', -+ 'common/gpu/media/vaapi_video_decode_accelerator_tizen.h', -+ 'common/gpu/media/vaapi_wrapper.cc', -+ 'common/gpu/media/vaapi_wrapper.h', -+ ], -+ 'include_dirs': [ -+ '<(DEPTH)/third_party/libva', -+ '<(DEPTH)/third_party/khronos', -+ ], -+ 'link_settings': { -+ 'libraries': [ -+ '-lEGL', -+ '-lGLESv2', -+ ], -+ }, -+ }], - ['OS=="win"', { - 'dependencies': [ - '../media/media.gyp:media', -diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc -index 9e29e03..2c04d40 100644 ---- a/content/gpu/gpu_main.cc -+++ b/content/gpu/gpu_main.cc -@@ -42,7 +42,8 @@ - #include "sandbox/win/src/sandbox.h" - #elif defined(OS_CHROMEOS) && defined(ARCH_CPU_ARMEL) && defined(USE_X11) - #include "content/common/gpu/media/exynos_video_decode_accelerator.h" --#elif defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY) && defined(USE_X11) -+#elif (defined(OS_CHROMEOS) || defined(OS_TIZEN_MOBILE)) &&\ -+ defined(ARCH_CPU_X86_FAMILY) && defined(USE_X11) - #include "content/common/gpu/media/vaapi_wrapper.h" - #endif - -@@ -360,7 +361,8 @@ bool WarmUpSandbox(const CommandLine& command_line) { - - #if defined(OS_CHROMEOS) && defined(ARCH_CPU_ARMEL) && defined(USE_X11) - ExynosVideoDecodeAccelerator::PreSandboxInitialization(); --#elif defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY) && defined(USE_X11) -+#elif (defined(OS_CHROMEOS) || defined(OS_TIZEN_MOBILE)) &&\ -+ defined(ARCH_CPU_X86_FAMILY) && defined(USE_X11) - VaapiWrapper::PreSandboxInitialization(); - #endif - --- -1.8.3.2 - diff --git a/src/xwalk/packaging/crosswalk.spec b/src/xwalk/packaging/crosswalk.spec index 3e2ed89..5a327fd 100644 --- a/src/xwalk/packaging/crosswalk.spec +++ b/src/xwalk/packaging/crosswalk.spec @@ -2,7 +2,7 @@ %bcond_with wayland Name: crosswalk -Version: 5.34.105.0 +Version: 6.34.113.0 Release: 0 Summary: Crosswalk is an app runtime based on Chromium License: (BSD-3-Clause and LGPL-2.1+) @@ -16,7 +16,6 @@ Source1001: crosswalk.manifest Source1002: %{name}.xml.in Source1003: %{name}.png Patch1: %{name}-do-not-look-for-gtk2-when-using-aura.patch -Patch7: %{name}-tizen-audio-session-manager.patch Patch8: %{name}-mesa-ozone-typedefs.patch Patch9: Blink-Add-GCC-flag-Wno-narrowing-fix-64bits-build.patch @@ -26,13 +25,13 @@ BuildRequires: expat-devel BuildRequires: flex BuildRequires: gperf BuildRequires: libcap-devel +BuildRequires: ninja BuildRequires: python BuildRequires: python-xml BuildRequires: perl BuildRequires: which BuildRequires: pkgconfig(alsa) BuildRequires: pkgconfig(appcore-common) -BuildRequires: pkgconfig(audio-session-mgr) BuildRequires: pkgconfig(cairo) BuildRequires: pkgconfig(capi-location-manager) BuildRequires: pkgconfig(dbus-1) @@ -107,7 +106,6 @@ cp -a src/LICENSE LICENSE.chromium cp -a src/xwalk/LICENSE LICENSE.xwalk %patch1 -%patch7 %if "%{tizen}" < "3.0" %patch2 @@ -143,34 +141,20 @@ export LDFLAGS="${LDFLAGS} -Wl,--no-keep-memory" # build root to the BUILDDIR_NAME definition, such as "/var/tmp/xwalk-build" # (remember all paths are still inside the chroot): # gbs build --define 'BUILDDIR_NAME /some/path' -# -# The --depth and --generator-output combo is used to put all the Makefiles -# inside the build directory, and (this is the important part) keep file lists -# (generatedwith <|() in gyp) in the build directory as well, otherwise they -# will be in the source directory, erased every time and trigger an almost full -# Blink rebuild (among other smaller targets). -# We cannot always pass those flags, though, because gyp's make generator does -# not work if the --generator-output is the top-level source directory. BUILDDIR_NAME="%{?BUILDDIR_NAME}" -if [ -z "${BUILDDIR_NAME}" ]; then - BUILDDIR_NAME="." -else - GYP_EXTRA_FLAGS="--depth=. --generator-output=${BUILDDIR_NAME}" +if [ -n "${BUILDDIR_NAME}" ]; then + mkdir -p "${BUILDDIR_NAME}" + ln -s "${BUILDDIR_NAME}" src/out fi %if %{with wayland} -GYP_EXTRA_FLAGS="${GYP_EXTRA_FLAGS} -Duse_ash=1 -Duse_ozone=1" +GYP_EXTRA_FLAGS="${GYP_EXTRA_FLAGS} -Duse_ozone=1" %endif -# Change src/ so that we can pass "." to --depth below, otherwise we would need -# to pass "src" to it, but this confuses the gyp make generator, that expects -# to be called from the root source directory. -cd src - # --no-parallel is added because chroot does not mount a /dev/shm, this will # cause python multiprocessing.SemLock error. -export GYP_GENERATORS='make' -./xwalk/gyp_xwalk xwalk/xwalk.gyp \ +export GYP_GENERATORS='ninja' +./src/xwalk/gyp_xwalk src/xwalk/xwalk.gyp \ --no-parallel \ ${GYP_EXTRA_FLAGS} \ -Dchromeos=0 \ @@ -182,55 +166,32 @@ ${GYP_EXTRA_FLAGS} \ -Duse_gconf=0 \ -Duse_kerberos=0 \ -Duse_system_bzip2=1 \ --Duse_system_icu=1 \ -Duse_system_libexif=1 \ -Duse_system_libxml=1 \ -Duse_system_nspr=1 \ --Denable_xi21_mt=1 \ --Duse_xi2_mt=0 \ -Denable_hidpi=1 -make %{?_smp_mflags} -C "${BUILDDIR_NAME}" BUILDTYPE=Release xwalk xwalkctl xwalk_launcher xwalk-pkg-helper +ninja %{?_smp_mflags} -C src/out/Release xwalk xwalkctl xwalk_launcher xwalk-pkg-helper %install -# Support building in a non-standard directory, possibly outside %{_builddir}. -# Since the build root is erased every time a new build is performed, one way -# to avoid losing the build directory is to specify a location outside the -# build root to the BUILDDIR_NAME definition, such as "/var/tmp/xwalk-build" -# (remember all paths are still inside the chroot): -# gbs build --define 'BUILDDIR_NAME /some/path' -BUILDDIR_NAME="%{?BUILDDIR_NAME}" -if [ -z "${BUILDDIR_NAME}" ]; then - BUILDDIR_NAME="." -fi - -# Since BUILDDIR_NAME can be either a relative path or an absolute one, we need -# to cd into src/ so that it means the same thing in the build and install -# stages: during the former, a relative location refers to a place inside src/, -# whereas during the latter a relative location by default would refer to a -# place one directory above src/. If BUILDDIR_NAME is an absolute path, this is -# irrelevant anyway. -cd src - # Binaries. -install -p -D ../xwalk %{buildroot}%{_bindir}/xwalk +install -p -D xwalk %{buildroot}%{_bindir}/xwalk install -p -D %{SOURCE2} %{buildroot}%{_dbusservicedir}/org.crosswalkproject.Runtime1.service -install -p -D ../xwalk.service %{buildroot}%{_systemduserservicedir}/xwalk.service -install -p -D ${BUILDDIR_NAME}/out/Release/xwalk %{buildroot}%{_libdir}/xwalk/xwalk -install -p -D ${BUILDDIR_NAME}/out/Release/xwalkctl %{buildroot}%{_bindir}/xwalkctl -install -p -D ${BUILDDIR_NAME}/out/Release/xwalk-launcher %{buildroot}%{_bindir}/xwalk-launcher +install -p -D xwalk.service %{buildroot}%{_systemduserservicedir}/xwalk.service +install -p -D src/out/Release/xwalk %{buildroot}%{_libdir}/xwalk/xwalk +install -p -D src/out/Release/xwalkctl %{buildroot}%{_bindir}/xwalkctl +install -p -D src/out/Release/xwalk-launcher %{buildroot}%{_bindir}/xwalk-launcher # xwalk-pkg-helper needs to be set-user-ID-root so it can finish the installation process. -install -m 06755 -p -D ${BUILDDIR_NAME}/out/Release/xwalk-pkg-helper %{buildroot}%{_bindir}/xwalk-pkg-helper +install -m 06755 -p -D src/out/Release/xwalk-pkg-helper %{buildroot}%{_bindir}/xwalk-pkg-helper # Supporting libraries and resources. -install -p -D ${BUILDDIR_NAME}/out/Release/icudtl.dat %{buildroot}%{_libdir}/xwalk/icudtl.dat -install -p -D ${BUILDDIR_NAME}/out/Release/libffmpegsumo.so %{buildroot}%{_libdir}/xwalk/libffmpegsumo.so -install -p -D ${BUILDDIR_NAME}/out/Release/xwalk.pak %{buildroot}%{_libdir}/xwalk/xwalk.pak +install -p -D src/out/Release/icudtl.dat %{buildroot}%{_libdir}/xwalk/icudtl.dat +install -p -D src/out/Release/libffmpegsumo.so %{buildroot}%{_libdir}/xwalk/libffmpegsumo.so +install -p -D src/out/Release/xwalk.pak %{buildroot}%{_libdir}/xwalk/xwalk.pak # Register xwalk to the package manager. -install -p -D ../%{name}.xml %{buildroot}%{_manifestdir}/%{name}.xml -install -p -D ../%{name}.png %{buildroot}%{_desktop_icondir}/%{name}.png - +install -p -D %{name}.xml %{buildroot}%{_manifestdir}/%{name}.xml +install -p -D %{name}.png %{buildroot}%{_desktop_icondir}/%{name}.png %post mkdir -p %{_desktop_icondir_ro} diff --git a/src/xwalk/runtime/android/core/src/org/xwalk/core/SslErrorHandler.java b/src/xwalk/runtime/android/core/src/org/xwalk/core/SslErrorHandler.java deleted file mode 100644 index ccf12a7..0000000 --- a/src/xwalk/runtime/android/core/src/org/xwalk/core/SslErrorHandler.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * 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. - */ - -package org.xwalk.core; - -import android.os.Handler; - -/** - * SslErrorHandler: class responsible for handling SSL errors. - * This class is passed as a parameter to BrowserCallback.displaySslErrorDialog - * and is meant to receive the user's response. - */ -public class SslErrorHandler extends Handler { - - /** - * @hide Only for use by WebViewProvider implementations. - */ - public SslErrorHandler() {} - - /** - * Proceed with the SSL certificate. - */ - public void proceed() {} - - /** - * Cancel this request and all pending requests for the XWalkView that had - * the error. - */ - public void cancel() {} -} diff --git a/src/xwalk/runtime/android/core/src/org/xwalk/core/SslUtil.java b/src/xwalk/runtime/android/core/src/org/xwalk/core/SslUtil.java new file mode 100644 index 0000000..430daf7 --- /dev/null +++ b/src/xwalk/runtime/android/core/src/org/xwalk/core/SslUtil.java @@ -0,0 +1,62 @@ +// Copyright 2013 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. + +package org.xwalk.core; + +import android.net.http.SslCertificate; +import android.net.http.SslError; +import android.util.Log; + +import org.chromium.net.NetError; +import org.chromium.net.X509Util; + +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +class SslUtil { + private static final String TAG = "SslUtil"; + + /** + * Creates an SslError object from a chromium net error code. + */ + public static SslError sslErrorFromNetErrorCode(int error, SslCertificate cert, String url) { + assert (error >= NetError.ERR_CERT_END && error <= NetError.ERR_CERT_COMMON_NAME_INVALID); + switch(error) { + case NetError.ERR_CERT_COMMON_NAME_INVALID: + return new SslError(SslError.SSL_IDMISMATCH, cert, url); + case NetError.ERR_CERT_DATE_INVALID: + return new SslError(SslError.SSL_DATE_INVALID, cert, url); + case NetError.ERR_CERT_AUTHORITY_INVALID: + return new SslError(SslError.SSL_UNTRUSTED, cert, url); + default: + break; + } + // Map all other codes to SSL_INVALID. + return new SslError(SslError.SSL_INVALID, cert, url); + } + + public static SslCertificate getCertificateFromDerBytes(byte[] derBytes) { + if (derBytes == null) { + return null; + } + + try { + X509Certificate x509Certificate = + X509Util.createCertificateFromBytes(derBytes); + return new SslCertificate(x509Certificate); + } catch (CertificateException e) { + // A SSL related exception must have occured. This shouldn't happen. + Log.w(TAG, "Could not read certificate: " + e); + } catch (KeyStoreException e) { + // A SSL related exception must have occured. This shouldn't happen. + Log.w(TAG, "Could not read certificate: " + e); + } catch (NoSuchAlgorithmException e) { + // A SSL related exception must have occured. This shouldn't happen. + Log.w(TAG, "Could not read certificate: " + e); + } + return null; + } +} diff --git a/src/xwalk/runtime/android/core/src/org/xwalk/core/XWalkClient.java b/src/xwalk/runtime/android/core/src/org/xwalk/core/XWalkClient.java index c27fa1d..74f7145 100644 --- a/src/xwalk/runtime/android/core/src/org/xwalk/core/XWalkClient.java +++ b/src/xwalk/runtime/android/core/src/org/xwalk/core/XWalkClient.java @@ -20,6 +20,7 @@ import android.graphics.Bitmap; import android.net.http.SslError; import android.os.Message; import android.view.KeyEvent; +import android.webkit.ValueCallback; import android.webkit.WebResourceResponse; public class XWalkClient { @@ -185,13 +186,14 @@ public class XWalkClient { * load. * * @param view The XWalkView that is initiating the callback. - * @param handler An SslErrorHandler object that will handle the user's - * response. + * @param callback The callback class. Passing 'true' means accepting the + * ssl error and continue to load. Passing 'false' means + * forbidding to load the web page. * @param error The SSL error object. */ - public void onReceivedSslError(XWalkView view, SslErrorHandler handler, + public void onReceivedSslError(XWalkView view, ValueCallback callback, SslError error) { - handler.cancel(); + callback.onReceiveValue(true); } /** diff --git a/src/xwalk/runtime/android/core/src/org/xwalk/core/XWalkContent.java b/src/xwalk/runtime/android/core/src/org/xwalk/core/XWalkContent.java index b4fe314..18cb146 100644 --- a/src/xwalk/runtime/android/core/src/org/xwalk/core/XWalkContent.java +++ b/src/xwalk/runtime/android/core/src/org/xwalk/core/XWalkContent.java @@ -356,6 +356,11 @@ public class XWalkContent extends FrameLayout { loadUrl(url); } + @CalledByNative + public void onGetFullscreenFlagFromManifest(boolean enterFullscreen) { + if (enterFullscreen) getXWalkWebChromeClient().onToggleFullscreen(true); + } + public void destroy() { if (mXWalkContent == 0) return; diff --git a/src/xwalk/runtime/android/core/src/org/xwalk/core/XWalkContentsClient.java b/src/xwalk/runtime/android/core/src/org/xwalk/core/XWalkContentsClient.java index a52d61f..bd18870 100644 --- a/src/xwalk/runtime/android/core/src/org/xwalk/core/XWalkContentsClient.java +++ b/src/xwalk/runtime/android/core/src/org/xwalk/core/XWalkContentsClient.java @@ -126,7 +126,7 @@ public abstract class XWalkContentsClient extends ContentViewClient { public abstract void onReceivedHttpAuthRequest(XWalkHttpAuthHandler handler, String host, String realm); - public abstract void onReceivedSslError(SslErrorHandler handler, SslError error); + public abstract void onReceivedSslError(ValueCallback callback, SslError error); public abstract void onReceivedLoginRequest(String realm, String account, String args); diff --git a/src/xwalk/runtime/android/core/src/org/xwalk/core/XWalkContentsClientBridge.java b/src/xwalk/runtime/android/core/src/org/xwalk/core/XWalkContentsClientBridge.java index 3b5ee39..98f4dc7 100644 --- a/src/xwalk/runtime/android/core/src/org/xwalk/core/XWalkContentsClientBridge.java +++ b/src/xwalk/runtime/android/core/src/org/xwalk/core/XWalkContentsClientBridge.java @@ -157,10 +157,12 @@ class XWalkContentsClientBridge extends XWalkContentsClient if (mXWalkClient != null && mXWalkView != null) mXWalkClient.onReceivedHttpAuthRequest(mXWalkView, handler, host, realm); } + @Override - public void onReceivedSslError(SslErrorHandler handler, SslError error) { - if (mXWalkClient != null && mXWalkView != null) - mXWalkClient.onReceivedSslError(mXWalkView, handler, error); + public void onReceivedSslError(ValueCallback callback, SslError error) { + if (mXWalkClient != null && mXWalkView != null) { + mXWalkClient.onReceivedSslError(mXWalkView, callback, error); + } } @Override @@ -375,8 +377,20 @@ class XWalkContentsClientBridge extends XWalkContentsClient @CalledByNative private boolean allowCertificateError(int certError, byte[] derBytes, final String url, final int id) { - // TODO(yongsheng): Implement this. - return false; + final SslCertificate cert = SslUtil.getCertificateFromDerBytes(derBytes); + if (cert == null) { + // if the certificate or the client is null, cancel the request + return false; + } + final SslError sslError = SslUtil.sslErrorFromNetErrorCode(certError, cert, url); + ValueCallback callback = new ValueCallback() { + @Override + public void onReceiveValue(Boolean value) { + proceedSslError(value.booleanValue(), id); + } + }; + onReceivedSslError(callback, sslError); + return true; } private void proceedSslError(boolean proceed, int id) { diff --git a/src/xwalk/runtime/android/core/src/org/xwalk/core/XWalkDefaultClient.java b/src/xwalk/runtime/android/core/src/org/xwalk/core/XWalkDefaultClient.java index fbdf296..0257f80 100644 --- a/src/xwalk/runtime/android/core/src/org/xwalk/core/XWalkDefaultClient.java +++ b/src/xwalk/runtime/android/core/src/org/xwalk/core/XWalkDefaultClient.java @@ -14,18 +14,12 @@ import android.os.Message; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; +import android.webkit.ValueCallback; import android.widget.EditText; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.TextView; -import org.xwalk.core.HttpAuthDatabase; -import org.xwalk.core.XWalkHttpAuthHandler; -import org.xwalk.core.R; -import org.xwalk.core.SslErrorHandler; -import org.xwalk.core.XWalkClient; -import org.xwalk.core.XWalkView; - // TODO(yongsheng): remove public modifier. public class XWalkDefaultClient extends XWalkClient { @@ -78,23 +72,22 @@ public class XWalkDefaultClient extends XWalkClient { } @Override - public void onReceivedSslError(XWalkView view, SslErrorHandler handler, + public void onReceivedSslError(XWalkView view, ValueCallback callback, SslError error) { - final SslErrorHandler sslHandler = handler; + final ValueCallback valueCallback = callback; AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext); + // Don't use setOnDismissListener because it's supported since API level 17. dialogBuilder.setTitle(R.string.ssl_alert_title) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - sslHandler.proceed(); + valueCallback.onReceiveValue(true); dialog.dismiss(); } - }) - .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - sslHandler.cancel(); - dialog.cancel(); + }).setNegativeButton(android.R.string.cancel, null) + .setOnCancelListener(new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + valueCallback.onReceiveValue(false); } }); mDialog = dialogBuilder.create(); diff --git a/src/xwalk/runtime/android/core/src/org/xwalk/core/extension/api/contacts/ContactFinder.java b/src/xwalk/runtime/android/core/src/org/xwalk/core/extension/api/contacts/ContactFinder.java index bb17ec2..7301141 100644 --- a/src/xwalk/runtime/android/core/src/org/xwalk/core/extension/api/contacts/ContactFinder.java +++ b/src/xwalk/runtime/android/core/src/org/xwalk/core/extension/api/contacts/ContactFinder.java @@ -6,6 +6,8 @@ package org.xwalk.core.extension.api.contacts; import android.content.ContentResolver; import android.database.Cursor; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.CommonDataKinds.Email; import android.provider.ContactsContract.CommonDataKinds.Event; @@ -261,10 +263,11 @@ public class ContactFinder { long id = c.getLong(c.getColumnIndex(Data.CONTACT_ID)); if (!dataMap.containsKey(id)) dataMap.put(id, new ContactData()); ContactData d = dataMap.get(id); + if (d.lastUpdated == null && VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) { + d.lastUpdated = mUtils.getLastUpdated(id); + } String mime = c.getString(c.getColumnIndex(Data.MIMETYPE)); - if (mime.equals(ContactConstants.CUSTOM_MIMETYPE_LASTUPDATED)) { - d.lastUpdated = c.getString(c.getColumnIndex(Data.DATA1)); - } else if (mime.equals(StructuredName.CONTENT_ITEM_TYPE)) { + if (mime.equals(StructuredName.CONTENT_ITEM_TYPE)) { d.oName = addString(d.oName, c, "displayName", StructuredName.DISPLAY_NAME); d.oName = addArrayTop(d.oName, c, "honorificPrefixes", StructuredName.PREFIX); d.oName = addArrayTop(d.oName, c, "givenNames", StructuredName.GIVEN_NAME); diff --git a/src/xwalk/runtime/android/core/src/org/xwalk/core/extension/api/contacts/ContactSaver.java b/src/xwalk/runtime/android/core/src/org/xwalk/core/extension/api/contacts/ContactSaver.java index 133fb1e..074686e 100644 --- a/src/xwalk/runtime/android/core/src/org/xwalk/core/extension/api/contacts/ContactSaver.java +++ b/src/xwalk/runtime/android/core/src/org/xwalk/core/extension/api/contacts/ContactSaver.java @@ -170,10 +170,6 @@ public class ContactSaver { } } - private void buildByDate(String name, String mimeType, String data) { - buildByDate(name, mimeType, data, null, 0); - } - private void buildByDate(String name, String mimeType, String data, String type, int dateType) { if (!mContact.has(name)) return; @@ -200,12 +196,12 @@ public class ContactSaver { } } - private void PutToContact(String id) { - if (id == null) return; + private void PutToContact(String name, String value) { + if (name == null) return; try { - mContact.put("id", id); + mContact.put(name, value); } catch (JSONException e) { - Log.e(TAG, "Failed to put id " + id + " into contact" + e.toString()); + Log.e(TAG, "Failed to set " + name + " = " + value + " for contact" + e.toString()); } } @@ -281,7 +277,6 @@ public class ContactSaver { } } - buildByDate("lastUpdated", ContactConstants.CUSTOM_MIMETYPE_LASTUPDATED, Data.DATA1); buildByEvent("birthday", Event.TYPE_BIRTHDAY); buildByEvent("anniversary", Event.TYPE_ANNIVERSARY); @@ -311,8 +306,11 @@ public class ContactSaver { + "new raw ids are: " + newRawIds.toString()); return mContact; } - String id = mUtils.getId(newRawIds.iterator().next()); - PutToContact(id); + mId = mUtils.getId(newRawIds.iterator().next()); + PutToContact("id", mId); + } + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2 ) { + PutToContact("lastUpdated", String.valueOf(mUtils.getLastUpdated(Long.valueOf(mId)))); } return mContact; } diff --git a/src/xwalk/runtime/android/core/src/org/xwalk/core/extension/api/contacts/ContactUtils.java b/src/xwalk/runtime/android/core/src/org/xwalk/core/extension/api/contacts/ContactUtils.java index a850f4b..9ad8b27 100644 --- a/src/xwalk/runtime/android/core/src/org/xwalk/core/extension/api/contacts/ContactUtils.java +++ b/src/xwalk/runtime/android/core/src/org/xwalk/core/extension/api/contacts/ContactUtils.java @@ -108,6 +108,29 @@ public class ContactUtils { } } + /** + * Get lastUpdatedTimestamp and return as JS date format + * @param long e.g. 987654321012 + * @return string e.g. "2001-04-19T04:25:21.012Z" + */ + @android.annotation.TargetApi(android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) + public String getLastUpdated(long contactId) { + String[] projection = new String[]{ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP}; + + Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contactId); + Cursor cursor = mResolver.query(uri, projection, null, null, null); + try { + if (cursor.moveToNext()) { + return timeConvertToJS(cursor.getLong(0)); + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + return null; + } + public Set getCurrentRawIds() { Cursor c = null; try { @@ -277,4 +300,15 @@ public class ContactUtils { } return date; } + + /** + * Convert epoch seconds to JS date format + * @param long e.g. 61 + * @return string e.g. "1969-12-31T00:01:01Z" + */ + private String timeConvertToJS(long seconds) { + final SimpleDateFormat df = + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", java.util.Locale.getDefault()); + return df.format(new java.util.Date(seconds)); + } } diff --git a/src/xwalk/runtime/android/core/src/org/xwalk/core/extension/api/contacts/contacts_api.js b/src/xwalk/runtime/android/core/src/org/xwalk/core/extension/api/contacts/contacts_api.js index baad915..525e03f 100644 --- a/src/xwalk/runtime/android/core/src/org/xwalk/core/extension/api/contacts/contacts_api.js +++ b/src/xwalk/runtime/android/core/src/org/xwalk/core/extension/api/contacts/contacts_api.js @@ -175,7 +175,7 @@ window.ContactName = function(init) { window.Contact = function(init) { this.id = null; - this.lastUpdated = null; + this.lastUpdated = new Date(); this.name = init.name; this.emails = init.emails; this.photos = init.photos; diff --git a/src/xwalk/runtime/android/core/strings/android_xwalk_strings.grd b/src/xwalk/runtime/android/core/strings/android_xwalk_strings.grd index 1a478ef..aa01c41 100644 --- a/src/xwalk/runtime/android/core/strings/android_xwalk_strings.grd +++ b/src/xwalk/runtime/android/core/strings/android_xwalk_strings.grd @@ -27,7 +27,7 @@ Finished to download file. - Ssl Alert + Ssl Certificate Error Alert Authentication Required diff --git a/src/xwalk/runtime/android/runtime/src/org/xwalk/runtime/XWalkClientForTest.java b/src/xwalk/runtime/android/runtime/src/org/xwalk/runtime/XWalkClientForTest.java index 0fb96f5..a2de6ee 100644 --- a/src/xwalk/runtime/android/runtime/src/org/xwalk/runtime/XWalkClientForTest.java +++ b/src/xwalk/runtime/android/runtime/src/org/xwalk/runtime/XWalkClientForTest.java @@ -7,11 +7,11 @@ package org.xwalk.runtime; import android.content.Context; import android.graphics.Bitmap; import android.net.http.SslError; +import android.webkit.ValueCallback; import java.lang.reflect.Method; import org.xwalk.core.XWalkDefaultClient; -import org.xwalk.core.SslErrorHandler; import org.xwalk.core.XWalkView; class XWalkClientForTest extends XWalkDefaultClient { @@ -37,14 +37,14 @@ class XWalkClientForTest extends XWalkDefaultClient { } @Override - public void onReceivedSslError(XWalkView view, SslErrorHandler handler, + public void onReceivedSslError(XWalkView view, ValueCallback callback, SslError error) { if (mCallbackForTest != null) { try { Class objectClass = mCallbackForTest.getClass(); Method onReceivedSslError = objectClass.getMethod( - "onReceivedSslError", SslErrorHandler.class, SslError.class); - onReceivedSslError.invoke(mCallbackForTest, handler, error); + "onReceivedSslError", ValueCallback.class, SslError.class); + onReceivedSslError.invoke(mCallbackForTest, callback, error); } catch (Exception e) { e.printStackTrace(); } diff --git a/src/xwalk/runtime/app/android/xwalk_main_delegate_android.cc b/src/xwalk/runtime/app/android/xwalk_main_delegate_android.cc index 9e36cb1..7fa476e 100644 --- a/src/xwalk/runtime/app/android/xwalk_main_delegate_android.cc +++ b/src/xwalk/runtime/app/android/xwalk_main_delegate_android.cc @@ -54,15 +54,6 @@ int XWalkMainDelegateAndroid::RunProcess( } void XWalkMainDelegateAndroid::InitResourceBundle() { - int pak_fd = - base::GlobalDescriptors::GetInstance()->MaybeGet(kXWalkPakDescriptor); - if (pak_fd != base::kInvalidPlatformFileValue) { - ui::ResourceBundle::InitSharedInstanceWithPakFile( - base::File(pak_fd), false); - ResourceBundle::GetSharedInstance().AddDataPackFromFile( - base::File(pak_fd), ui::SCALE_FACTOR_100P); - return; - } base::FilePath pak_file; base::FilePath pak_dir; bool got_path = PathService::Get(base::DIR_ANDROID_APP_DATA, &pak_dir); diff --git a/src/xwalk/runtime/browser/android/renderer_host/xwalk_render_view_host_ext.cc b/src/xwalk/runtime/browser/android/renderer_host/xwalk_render_view_host_ext.cc index 3de726c..96a1ab2 100644 --- a/src/xwalk/runtime/browser/android/renderer_host/xwalk_render_view_host_ext.cc +++ b/src/xwalk/runtime/browser/android/renderer_host/xwalk_render_view_host_ext.cc @@ -99,11 +99,8 @@ void XWalkRenderViewHostExt::DidNavigateAnyFrame( const content::FrameNavigateParams& params) { DCHECK(CalledOnValidThread()); - // TODO(Xingnan): Add the AddVisitedURLs method - // in RuntimeContext. - - // RuntimeContext::FromWebContents(web_contents()) - // ->AddVisitedURLs(params.redirects); + RuntimeContext::FromWebContents(web_contents()) + ->AddVisitedURLs(params.redirects); } bool XWalkRenderViewHostExt::OnMessageReceived(const IPC::Message& message) { diff --git a/src/xwalk/runtime/browser/android/xwalk_content.cc b/src/xwalk/runtime/browser/android/xwalk_content.cc index d62009e..a4893f7 100644 --- a/src/xwalk/runtime/browser/android/xwalk_content.cc +++ b/src/xwalk/runtime/browser/android/xwalk_content.cc @@ -44,6 +44,7 @@ using base::android::ScopedJavaLocalRef; using content::BrowserThread; using content::WebContents; using navigation_interception::InterceptNavigationDelegate; +using xwalk::application_manifest_keys::kDisplay; namespace xwalk { @@ -216,13 +217,9 @@ jboolean XWalkContent::SetManifest(JNIEnv* env, // According to original proposal for "app:launch:local_path", the "http" // and "https" schemes are supported. So |url| should do nothing when it // already has "http" or "https" scheme. - std::string lower_url = url; - std::transform(lower_url.begin(), lower_url.end(), - lower_url.begin(), std::tolower); - if (lower_url.find(content::kHttpScheme) == std::string::npos && - lower_url.find(content::kHttpsScheme) == std::string::npos) { + std::string scheme = GURL(url).scheme(); + if (scheme != content::kHttpScheme && scheme != content::kHttpsScheme) url = path_str + url; - } } else { manifest.GetString( xwalk::application_manifest_keys::kLaunchWebURLKey, &url); @@ -239,6 +236,17 @@ jboolean XWalkContent::SetManifest(JNIEnv* env, ScopedJavaLocalRef url_buffer = base::android::ConvertUTF8ToJavaString(env, url); + if (manifest.HasPath(kDisplay)) { + std::string display_string; + manifest.GetString(kDisplay, &display_string); + // TODO(David): update the handling process of the display strings + // including fullscreen etc. + bool display_as_fullscreen = ( + display_string.find("fullscreen") != std::string::npos); + Java_XWalkContent_onGetFullscreenFlagFromManifest( + env, obj, display_as_fullscreen ? JNI_TRUE : JNI_FALSE); + } + // Check whether need to display launch screen. (Read from manifest.json) if (manifest.HasPath( xwalk::application_manifest_keys::kLaunchScreen)) { diff --git a/src/xwalk/runtime/browser/android/xwalk_contents_client_bridge.cc b/src/xwalk/runtime/browser/android/xwalk_contents_client_bridge.cc index ac7bfe4..b0779d6 100644 --- a/src/xwalk/runtime/browser/android/xwalk_contents_client_bridge.cc +++ b/src/xwalk/runtime/browser/android/xwalk_contents_client_bridge.cc @@ -40,7 +40,7 @@ void RunUpdateNotificationIconOnUIThread( int route_id, const SkBitmap& icon) { XWalkContentsClientBridgeBase* bridge = - XWalkContentsClientBridgeBase::FromID(process_id, route_id); + XWalkContentsClientBridgeBase::FromRenderViewID(process_id, route_id); if (bridge) bridge->UpdateNotificationIcon(notification_id, icon); } diff --git a/src/xwalk/runtime/browser/android/xwalk_contents_client_bridge_base.cc b/src/xwalk/runtime/browser/android/xwalk_contents_client_bridge_base.cc index 89a8d24..4481431 100644 --- a/src/xwalk/runtime/browser/android/xwalk_contents_client_bridge_base.cc +++ b/src/xwalk/runtime/browser/android/xwalk_contents_client_bridge_base.cc @@ -6,6 +6,7 @@ #include "xwalk/runtime/browser/android/xwalk_contents_client_bridge_base.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" @@ -55,7 +56,7 @@ XWalkContentsClientBridgeBase* XWalkContentsClientBridgeBase::FromWebContents( } // static -XWalkContentsClientBridgeBase* XWalkContentsClientBridgeBase::FromID( +XWalkContentsClientBridgeBase* XWalkContentsClientBridgeBase::FromRenderViewID( int render_process_id, int render_view_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -67,6 +68,19 @@ XWalkContentsClientBridgeBase* XWalkContentsClientBridgeBase::FromID( return UserData::GetContents(web_contents); } +// static +XWalkContentsClientBridgeBase* XWalkContentsClientBridgeBase::FromRenderFrameID( + int render_process_id, + int render_frame_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + content::RenderFrameHost* rfh = + content::RenderFrameHost::FromID(render_process_id, render_frame_id); + if (!rfh) return NULL; + content::WebContents* web_contents = + content::WebContents::FromRenderFrameHost(rfh); + return UserData::GetContents(web_contents); +} + XWalkContentsClientBridgeBase::~XWalkContentsClientBridgeBase() { } diff --git a/src/xwalk/runtime/browser/android/xwalk_contents_client_bridge_base.h b/src/xwalk/runtime/browser/android/xwalk_contents_client_bridge_base.h index 555e890..a967548 100644 --- a/src/xwalk/runtime/browser/android/xwalk_contents_client_bridge_base.h +++ b/src/xwalk/runtime/browser/android/xwalk_contents_client_bridge_base.h @@ -35,8 +35,10 @@ class XWalkContentsClientBridgeBase { XWalkContentsClientBridgeBase* handler); static XWalkContentsClientBridgeBase* FromWebContents( content::WebContents* web_contents); - static XWalkContentsClientBridgeBase* FromID(int render_process_id, + static XWalkContentsClientBridgeBase* FromRenderViewID(int render_process_id, int render_view_id); + static XWalkContentsClientBridgeBase* FromRenderFrameID(int render_process_id, + int render_frame_id); virtual ~XWalkContentsClientBridgeBase(); diff --git a/src/xwalk/runtime/browser/android/xwalk_cookie_access_policy.cc b/src/xwalk/runtime/browser/android/xwalk_cookie_access_policy.cc index 2eef174..140c46b 100644 --- a/src/xwalk/runtime/browser/android/xwalk_cookie_access_policy.cc +++ b/src/xwalk/runtime/browser/android/xwalk_cookie_access_policy.cc @@ -57,7 +57,7 @@ bool XWalkCookieAccessPolicy::AllowGetCookie( const net::CookieList& cookie_list, content::ResourceContext* context, int render_process_id, - int render_view_id) { + int render_frame_id) { return GetGlobalAllowAccess(); } @@ -67,7 +67,7 @@ bool XWalkCookieAccessPolicy::AllowSetCookie( const std::string& cookie_line, content::ResourceContext* context, int render_process_id, - int render_view_id, + int render_frame_id, net::CookieOptions* options) { return GetGlobalAllowAccess(); } diff --git a/src/xwalk/runtime/browser/android/xwalk_cookie_access_policy.h b/src/xwalk/runtime/browser/android/xwalk_cookie_access_policy.h index 7104d4b..2574328 100644 --- a/src/xwalk/runtime/browser/android/xwalk_cookie_access_policy.h +++ b/src/xwalk/runtime/browser/android/xwalk_cookie_access_policy.h @@ -50,13 +50,13 @@ class XWalkCookieAccessPolicy { const net::CookieList& cookie_list, content::ResourceContext* context, int render_process_id, - int render_view_id); + int render_frame_id); bool AllowSetCookie(const GURL& url, const GURL& first_party, const std::string& cookie_line, content::ResourceContext* context, int render_process_id, - int render_view_id, + int render_frame_id, net::CookieOptions* options); private: diff --git a/src/xwalk/runtime/browser/android/xwalk_request_interceptor.cc b/src/xwalk/runtime/browser/android/xwalk_request_interceptor.cc index d14f44f..404dd63 100644 --- a/src/xwalk/runtime/browser/android/xwalk_request_interceptor.cc +++ b/src/xwalk/runtime/browser/android/xwalk_request_interceptor.cc @@ -59,13 +59,13 @@ XWalkRequestInterceptor::QueryForInterceptedRequestData( const GURL& location, net::URLRequest* request) const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - int render_process_id, render_view_id; + int render_process_id, render_frame_id; if (!ResourceRequestInfo::GetRenderFrameForRequest( - request, &render_process_id, &render_view_id)) + request, &render_process_id, &render_frame_id)) return scoped_ptr(); scoped_ptr io_thread_client = - XWalkContentsIoThreadClient::FromID(render_process_id, render_view_id); + XWalkContentsIoThreadClient::FromID(render_process_id, render_frame_id); if (!io_thread_client.get()) return scoped_ptr(); diff --git a/src/xwalk/runtime/browser/runtime_context.cc b/src/xwalk/runtime/browser/runtime_context.cc index e1c7bad..bf847fc 100644 --- a/src/xwalk/runtime/browser/runtime_context.cc +++ b/src/xwalk/runtime/browser/runtime_context.cc @@ -11,6 +11,7 @@ #include "base/command_line.h" #include "base/logging.h" #include "base/path_service.h" +#include "components/visitedlink/browser/visitedlink_master.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/resource_context.h" #include "content/public/browser/storage_partition.h" @@ -68,6 +69,9 @@ class RuntimeContext::RuntimeResourceContext : public content::ResourceContext { RuntimeContext::RuntimeContext() : resource_context_(new RuntimeResourceContext) { InitWhileIOAllowed(); +#if defined(OS_ANDROID) + InitVisitedLinkMaster(); +#endif } RuntimeContext::~RuntimeContext() { @@ -229,6 +233,25 @@ void RuntimeContext::SetCSPString(const std::string& csp) { std::string RuntimeContext::GetCSPString() const { return csp_; } + +void RuntimeContext::InitVisitedLinkMaster() { + visitedlink_master_.reset( + new visitedlink::VisitedLinkMaster(this, this, false)); + visitedlink_master_->Init(); +} + +void RuntimeContext::AddVisitedURLs(const std::vector& urls) { + DCHECK(visitedlink_master_); + visitedlink_master_->AddURLs(urls); +} + +void RuntimeContext::RebuildTable( + const scoped_refptr& enumerator) { + // XWalkView rebuilds from XWalkWebChromeClient.getVisitedHistory. The client + // can change in the lifetime of this XWalkView and may not yet be set here. + // Therefore this initialization path is not used. + enumerator->OnComplete(true); +} #endif } // namespace xwalk diff --git a/src/xwalk/runtime/browser/runtime_context.h b/src/xwalk/runtime/browser/runtime_context.h index a1120e4..37ededa 100644 --- a/src/xwalk/runtime/browser/runtime_context.h +++ b/src/xwalk/runtime/browser/runtime_context.h @@ -9,10 +9,13 @@ #include #endif +#include + #include "base/compiler_specific.h" #include "base/files/file_path.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "components/visitedlink/browser/visitedlink_delegate.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/geolocation_permission_context.h" @@ -25,12 +28,21 @@ namespace content { class DownloadManagerDelegate; } +namespace visitedlink { +class VisitedLinkMaster; +} + namespace xwalk { class RuntimeDownloadManagerDelegate; class RuntimeURLRequestContextGetter; -class RuntimeContext : public content::BrowserContext { +class RuntimeContext + : public content::BrowserContext +#if defined(OS_ANDROID) + , public visitedlink::VisitedLinkDelegate +#endif +{ public: RuntimeContext(); virtual ~RuntimeContext(); @@ -88,6 +100,11 @@ class RuntimeContext : public content::BrowserContext { #if defined(OS_ANDROID) void SetCSPString(const std::string& csp); std::string GetCSPString() const; + // These methods map to Add methods in visitedlink::VisitedLinkMaster. + void AddVisitedURLs(const std::vector& urls); + // visitedlink::VisitedLinkDelegate implementation. + virtual void RebuildTable( + const scoped_refptr& enumerator) OVERRIDE; #endif private: @@ -97,6 +114,11 @@ class RuntimeContext : public content::BrowserContext { // allowed on the current thread. void InitWhileIOAllowed(); +#if defined(OS_ANDROID) + // Reset visitedlink master and initialize it. + void InitVisitedLinkMaster(); +#endif + scoped_ptr resource_context_; scoped_refptr download_manager_delegate_; scoped_refptr url_request_getter_; @@ -104,7 +126,9 @@ class RuntimeContext : public content::BrowserContext { geolocation_permission_context_; #if defined(OS_ANDROID) std::string csp_; + scoped_ptr visitedlink_master_; #endif + DISALLOW_COPY_AND_ASSIGN(RuntimeContext); }; diff --git a/src/xwalk/runtime/browser/ui/native_app_window_tizen.cc b/src/xwalk/runtime/browser/ui/native_app_window_tizen.cc index 9ff0627..2452dcd 100644 --- a/src/xwalk/runtime/browser/ui/native_app_window_tizen.cc +++ b/src/xwalk/runtime/browser/ui/native_app_window_tizen.cc @@ -21,9 +21,11 @@ namespace xwalk { NativeAppWindowTizen::NativeAppWindowTizen( const NativeAppWindow::CreateParams& create_params) : NativeAppWindowViews(create_params), +#if defined(OS_TIZEN_MOBILE) indicator_widget_(new TizenSystemIndicatorWidget()), + indicator_container_(new WidgetContainerView(indicator_widget_)), +#endif allowed_orientations_(ANY) { - indicator_container_.reset(new WidgetContainerView(indicator_widget_)); } void NativeAppWindowTizen::Initialize() { @@ -59,9 +61,11 @@ void NativeAppWindowTizen::ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) { if (details.is_add && details.child == this) { NativeAppWindowViews::ViewHierarchyChanged(details); +#if defined(OS_TIZEN_MOBILE) indicator_widget_->Initialize(GetNativeWindow()); top_view_layout()->set_top_view(indicator_container_.get()); AddChildView(indicator_container_.get()); +#endif } } @@ -224,7 +228,10 @@ void NativeAppWindowTizen::ApplyDisplayRotation() { if (!root_window->IsVisible()) return; UpdateTopViewOverlay(); + +#if defined(OS_TIZEN_MOBILE) indicator_widget_->SetDisplay(display_); +#endif root_window->SetTransform(GetRotationTransform()); } diff --git a/src/xwalk/runtime/browser/ui/native_app_window_tizen.h b/src/xwalk/runtime/browser/ui/native_app_window_tizen.h index aa7e764..cac7154 100644 --- a/src/xwalk/runtime/browser/ui/native_app_window_tizen.h +++ b/src/xwalk/runtime/browser/ui/native_app_window_tizen.h @@ -58,6 +58,7 @@ class NativeAppWindowTizen gfx::Display::Rotation GetClosestAllowedRotation( gfx::Display::Rotation) const; +#if defined(OS_TIZEN_MOBILE) // The system indicator is implemented as a widget because it needs to // receive events and may also be an overlay on top of the rest of the // content, regular views do not support this. We add it to the container, @@ -66,6 +67,8 @@ class NativeAppWindowTizen // The |indicator_widget_| is owned by the WidgetContainerView. TizenSystemIndicatorWidget* indicator_widget_; scoped_ptr indicator_container_; +#endif + gfx::Display display_; OrientationMask allowed_orientations_; diff --git a/src/xwalk/runtime/browser/ui/native_app_window_views.cc b/src/xwalk/runtime/browser/ui/native_app_window_views.cc index 96177a0..0e1506b 100644 --- a/src/xwalk/runtime/browser/ui/native_app_window_views.cc +++ b/src/xwalk/runtime/browser/ui/native_app_window_views.cc @@ -18,7 +18,7 @@ #include "ui/views/window/native_frame_view.h" #endif -#if defined(OS_TIZEN_MOBILE) +#if defined(OS_TIZEN) #include "xwalk/runtime/browser/ui/native_app_window_tizen.h" #endif @@ -294,7 +294,7 @@ void NativeAppWindowViews::OnWidgetBoundsChanged(views::Widget* widget, NativeAppWindow* NativeAppWindow::Create( const NativeAppWindow::CreateParams& create_params) { NativeAppWindowViews* window; -#if defined(OS_TIZEN_MOBILE) +#if defined(OS_TIZEN) window = new NativeAppWindowTizen(create_params); #else window = new NativeAppWindowViews(create_params); diff --git a/src/xwalk/runtime/browser/xwalk_browser_main_parts.cc b/src/xwalk/runtime/browser/xwalk_browser_main_parts.cc index c174005..7122e9e 100644 --- a/src/xwalk/runtime/browser/xwalk_browser_main_parts.cc +++ b/src/xwalk/runtime/browser/xwalk_browser_main_parts.cc @@ -61,7 +61,6 @@ GURL GetURLFromCommandLine(const CommandLine& command_line) { namespace xswitches { // Redefine settings not exposed by content module. -const char kEnableViewport[] = "enable-viewport"; const char kEnableOverlayScrollbars[] = "enable-overlay-scrollbars"; } @@ -91,7 +90,8 @@ XWalkBrowserMainParts::~XWalkBrowserMainParts() { void XWalkBrowserMainParts::PreMainMessageLoopStart() { CommandLine* command_line = CommandLine::ForCurrentProcess(); - command_line->AppendSwitch(xswitches::kEnableViewport); + command_line->AppendSwitch(switches::kEnableViewport); + command_line->AppendSwitch(switches::kEnableViewportMeta); command_line->AppendSwitch(xswitches::kEnableOverlayScrollbars); diff --git a/src/xwalk/runtime/browser/xwalk_content_browser_client.cc b/src/xwalk/runtime/browser/xwalk_content_browser_client.cc index 363b412..8fdcccc 100644 --- a/src/xwalk/runtime/browser/xwalk_content_browser_client.cc +++ b/src/xwalk/runtime/browser/xwalk_content_browser_client.cc @@ -16,6 +16,7 @@ #include "content/public/browser/web_contents.h" #include "content/public/common/main_function_params.h" #include "content/public/common/show_desktop_notification_params.h" +#include "net/ssl/ssl_info.h" #include "net/url_request/url_request_context_getter.h" #include "xwalk/extensions/common/xwalk_extension_switches.h" #include "xwalk/runtime/browser/xwalk_browser_main_parts.h" @@ -42,6 +43,13 @@ #endif #if defined(OS_TIZEN) +#include "xwalk/application/browser/application_system.h" +#include "xwalk/application/browser/application_service.h" +#include "xwalk/application/browser/application.h" +#include "xwalk/application/common/application_manifest_constants.h" +#include "xwalk/application/common/manifest_handlers/navigation_handler.h" +#include "xwalk/application/common/constants.h" +#include "xwalk/runtime/browser/runtime_platform_util.h" #include "xwalk/runtime/browser/xwalk_browser_main_parts_tizen.h" #endif @@ -157,7 +165,7 @@ bool XWalkContentBrowserClient::AllowGetCookie( const net::CookieList& cookie_list, content::ResourceContext* context, int render_process_id, - int render_view_id) { + int render_frame_id) { #if defined(OS_ANDROID) return XWalkCookieAccessPolicy::GetInstance()->AllowGetCookie( url, @@ -165,7 +173,7 @@ bool XWalkContentBrowserClient::AllowGetCookie( cookie_list, context, render_process_id, - render_view_id); + render_frame_id); #else return true; #endif @@ -177,7 +185,7 @@ bool XWalkContentBrowserClient::AllowSetCookie( const std::string& cookie_line, content::ResourceContext* context, int render_process_id, - int render_view_id, + int render_frame_id, net::CookieOptions* options) { #if defined(OS_ANDROID) return XWalkCookieAccessPolicy::GetInstance()->AllowSetCookie( @@ -186,13 +194,42 @@ bool XWalkContentBrowserClient::AllowSetCookie( cookie_line, context, render_process_id, - render_view_id, + render_frame_id, options); #else return true; #endif } +void XWalkContentBrowserClient::AllowCertificateError( + int render_process_id, + int render_frame_id, + int cert_error, + const net::SSLInfo& ssl_info, + const GURL& request_url, + ResourceType::Type resource_type, + bool overridable, + bool strict_enforcement, + const base::Callback& callback, + content::CertificateRequestResultType* result) { + // Currently only Android handles it. + // TODO(yongsheng): applies it for other platforms? +#if defined(OS_ANDROID) + XWalkContentsClientBridgeBase* client = + XWalkContentsClientBridgeBase::FromRenderFrameID(render_process_id, + render_frame_id); + bool cancel_request = true; + if (client) + client->AllowCertificateError(cert_error, + ssl_info.cert.get(), + request_url, + callback, + &cancel_request); + if (cancel_request) + *result = content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY; +#endif +} + void XWalkContentBrowserClient::RequestDesktopNotificationPermission( const GURL& source_origin, int callback_context, @@ -219,7 +256,8 @@ void XWalkContentBrowserClient::ShowDesktopNotification( bool worker) { #if defined(OS_ANDROID) XWalkContentsClientBridgeBase* bridge = - XWalkContentsClientBridgeBase::FromID(render_process_id, render_view_id); + XWalkContentsClientBridgeBase::FromRenderViewID(render_process_id, + render_view_id); bridge->ShowNotification(params, worker, render_process_id, render_view_id); #endif } @@ -230,35 +268,14 @@ void XWalkContentBrowserClient::CancelDesktopNotification( int notification_id) { #if defined(OS_ANDROID) XWalkContentsClientBridgeBase* bridge = - XWalkContentsClientBridgeBase::FromID(render_process_id, render_view_id); + XWalkContentsClientBridgeBase::FromRenderViewID(render_process_id, + render_view_id); bridge->CancelNotification( notification_id, render_process_id, render_view_id); #endif } #if defined(OS_ANDROID) -void XWalkContentBrowserClient::GetAdditionalMappedFilesForChildProcess( - const CommandLine& command_line, - int child_process_id, - std::vector* mappings) { - int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ; - base::FilePath pak_file; - bool r = PathService::Get(base::DIR_ANDROID_APP_DATA, &pak_file); - CHECK(r); - pak_file = pak_file.Append(FILE_PATH_LITERAL("paks")); - pak_file = pak_file.Append(FILE_PATH_LITERAL(kXWalkPakFilePath)); - - base::PlatformFile f = - base::CreatePlatformFile(pak_file, flags, NULL, NULL); - if (f == base::kInvalidPlatformFileValue) { - NOTREACHED() << "Failed to open file when creating renderer process: " - << "xwalk.pak"; - } - mappings->push_back( - content::FileDescriptorInfo(kXWalkPakDescriptor, - base::FileDescriptor(f, true))); -} - void XWalkContentBrowserClient::ResourceDispatcherHostCreated() { RuntimeResourceDispatcherHostDelegateAndroid:: ResourceDispatcherHostCreated(); @@ -270,4 +287,51 @@ content::SpeechRecognitionManagerDelegate* return new xwalk::XWalkSpeechRecognitionManagerDelegate(); } +#if defined(OS_TIZEN) +bool XWalkContentBrowserClient::CanCommitURL( + content::RenderProcessHost* process_host, const GURL& url) { + application::Application* app = xwalk_runner_->app_system()-> + application_service()->GetApplicationByRenderHostID( + process_host->GetID()); + DCHECK(app); + const application::ApplicationData* app_data =app->data(); + if (!app_data->HasCSPDefined() || + (url.SchemeIs(application::kApplicationScheme) && + url.host() == app_data->ID())) + return true; + + application::NavigationInfo* info = static_cast( + app_data->GetManifestData(application_widget_keys::kAllowNavigationKey)); + if (!info || !url.SchemeIsHTTPOrHTTPS()) { + LOG(INFO) << "[Block] Navigation link: " << url.spec(); + // FIXME: Blocked navigation link should be opened in system web browser, + // add corresponding code like this: + // platform_util::OpenExternal(url); + return false; + } + + // Check whether the navigation url domain is listed in WGT + // element, if yes, display it in web application, otherwise block the + // request. + const std::vector& allowed_list = info->GetAllowedDomains(); + for (std::vector::const_iterator it = allowed_list.begin(); + it != allowed_list.end(); ++it) { + if ((*it).find("*.") == 0 && + url.DomainIs((*it).substr(2).c_str())) { + LOG(INFO) << "[Allow] Navigation link: " << url.spec(); + return true; + } + + if (url.host() == *it) { + LOG(INFO) << "[Allow] Navigation link: " << url.spec(); + return true; + } + } + LOG(INFO) << "[Block] navigation link: " << url.spec(); + // FIXME: Should open blocked link in system web browser, need to add: + // platform_util::OpenExternal(url); + return false; +} +#endif + } // namespace xwalk diff --git a/src/xwalk/runtime/browser/xwalk_content_browser_client.h b/src/xwalk/runtime/browser/xwalk_content_browser_client.h index 32bf4c5..d5fc672 100644 --- a/src/xwalk/runtime/browser/xwalk_content_browser_client.h +++ b/src/xwalk/runtime/browser/xwalk_content_browser_client.h @@ -65,15 +65,27 @@ class XWalkContentBrowserClient : public content::ContentBrowserClient { const net::CookieList& cookie_list, content::ResourceContext* context, int render_process_id, - int render_view_id) OVERRIDE; + int render_frame_id) OVERRIDE; virtual bool AllowSetCookie(const GURL& url, const GURL& first_party, const std::string& cookie_line, content::ResourceContext* context, int render_process_id, - int render_view_id, + int render_frame_id, net::CookieOptions* options) OVERRIDE; + virtual void AllowCertificateError( + int render_process_id, + int render_frame_id, + int cert_error, + const net::SSLInfo& ssl_info, + const GURL& request_url, + ResourceType::Type resource_type, + bool overridable, + bool strict_enforcement, + const base::Callback& callback, + content::CertificateRequestResultType* result) OVERRIDE; + virtual content::SpeechRecognitionManagerDelegate* GetSpeechRecognitionManagerDelegate() OVERRIDE; @@ -96,12 +108,12 @@ class XWalkContentBrowserClient : public content::ContentBrowserClient { int render_process_id, int render_view_id, int notification_id) OVERRIDE; +#if defined(OS_TIZEN) + virtual bool CanCommitURL( + content::RenderProcessHost* process_host, const GURL& url) OVERRIDE; +#endif #if defined(OS_ANDROID) - virtual void GetAdditionalMappedFilesForChildProcess( - const CommandLine& command_line, - int child_process_id, - std::vector* mappings) OVERRIDE; virtual void ResourceDispatcherHostCreated(); #endif XWalkBrowserMainParts* main_parts() { return main_parts_; } diff --git a/src/xwalk/runtime/common/xwalk_common_message_generator.cc b/src/xwalk/runtime/common/xwalk_common_message_generator.cc new file mode 100644 index 0000000..01354c9 --- /dev/null +++ b/src/xwalk/runtime/common/xwalk_common_message_generator.cc @@ -0,0 +1,33 @@ +// Copyright (c) 2010 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. + +// Get basic type definitions. +#define IPC_MESSAGE_IMPL +#include "xwalk/runtime/common/xwalk_common_message_generator.h" + +// Generate constructors. +#include "ipc/struct_constructor_macros.h" +#include "xwalk/runtime/common/xwalk_common_message_generator.h" // NOLINT + +// Generate destructors. +#include "ipc/struct_destructor_macros.h" +#include "xwalk/runtime/common/xwalk_common_message_generator.h" // NOLINT + +// Generate param traits write methods. +#include "ipc/param_traits_write_macros.h" +namespace IPC { +#include "xwalk/runtime/common/xwalk_common_message_generator.h" // NOLINT +} // namespace IPC + +// Generate param traits read methods. +#include "ipc/param_traits_read_macros.h" +namespace IPC { +#include "xwalk/runtime/common/xwalk_common_message_generator.h" // NOLINT +} // namespace IPC + +// Generate param traits log methods. +#include "ipc/param_traits_log_macros.h" +namespace IPC { +#include "xwalk/runtime/common/xwalk_common_message_generator.h" // NOLINT +} // namespace IPC diff --git a/src/xwalk/runtime/common/xwalk_common_message_generator.h b/src/xwalk/runtime/common/xwalk_common_message_generator.h new file mode 100644 index 0000000..0b3b7fe --- /dev/null +++ b/src/xwalk/runtime/common/xwalk_common_message_generator.h @@ -0,0 +1,7 @@ +// 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. + +// Multiply-included file, hence no include guard. + +#include "xwalk/runtime/common/xwalk_common_messages.h" diff --git a/src/xwalk/runtime/common/xwalk_common_messages.cc b/src/xwalk/runtime/common/xwalk_common_messages.cc new file mode 100644 index 0000000..fb8e2c3 --- /dev/null +++ b/src/xwalk/runtime/common/xwalk_common_messages.cc @@ -0,0 +1,10 @@ +// 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. +#include "xwalk/runtime/common/xwalk_common_messages.h" + +namespace IPC { + +// TODO(upstream): - add enums and custom IPC traits here when needed. + +} // namespace IPC diff --git a/src/xwalk/runtime/common/xwalk_common_messages.h b/src/xwalk/runtime/common/xwalk_common_messages.h new file mode 100644 index 0000000..4afe4eb --- /dev/null +++ b/src/xwalk/runtime/common/xwalk_common_messages.h @@ -0,0 +1,35 @@ +// 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. + +// Multiply-included file, no traditional include guard. +#include "content/public/common/common_param_traits.h" +#include "ipc/ipc_channel_handle.h" +#include "ipc/ipc_message_macros.h" +#include "ipc/ipc_platform_file.h" +#include "url/gurl.h" + +// Singly-included section for enums and custom IPC traits. +#ifndef XWALK_RUNTIME_COMMON_XWALK_COMMON_MESSAGES_H_ +#define XWALK_RUNTIME_COMMON_XWALK_COMMON_MESSAGES_H_ + +namespace IPC { + +// TODO(upstream): - add enums and custom IPC traits here when needed. + +} // namespace IPC + +#endif // XWALK_RUNTIME_COMMON_XWALK_COMMON_MESSAGES_H_ + +#define IPC_MESSAGE_START ViewMsgStart + +//----------------------------------------------------------------------------- +// RenderView messages +// These are messages sent from the browser to the renderer process. + +IPC_MESSAGE_CONTROL3(ViewMsg_SetAccessWhiteList, // NOLINT + GURL /* source */, + GURL /* dest */, + bool /* allow_subdomains */) + +IPC_MESSAGE_CONTROL0(ViewMsg_EnableWarpMode) // NOLINT diff --git a/src/xwalk/runtime/common/xwalk_paths.cc b/src/xwalk/runtime/common/xwalk_paths.cc index 06de4ae..0727328 100644 --- a/src/xwalk/runtime/common/xwalk_paths.cc +++ b/src/xwalk/runtime/common/xwalk_paths.cc @@ -78,6 +78,11 @@ bool PathProvider(int key, base::FilePath* path) { cur = cur.Append(FILE_PATH_LITERAL("test")); cur = cur.Append(FILE_PATH_LITERAL("data")); break; + case xwalk::DIR_WGT_STORAGE_PATH: + if (!GetXWalkDataPath(&cur)) + return false; + cur = cur.Append(FILE_PATH_LITERAL("Widget Storage")); + break; default: return false; } diff --git a/src/xwalk/runtime/common/xwalk_paths.h b/src/xwalk/runtime/common/xwalk_paths.h index e007095..88520cc 100644 --- a/src/xwalk/runtime/common/xwalk_paths.h +++ b/src/xwalk/runtime/common/xwalk_paths.h @@ -15,6 +15,7 @@ enum { DIR_DATA_PATH = PATH_START, // Directory where the cache and local storage // data resides. DIR_TEST_DATA, // Directory where unit test data resides. + DIR_WGT_STORAGE_PATH, // Directory where widget storage data resides. PATH_END }; diff --git a/src/xwalk/runtime/renderer/xwalk_content_renderer_client.cc b/src/xwalk/runtime/renderer/xwalk_content_renderer_client.cc index e856a30..554a963 100644 --- a/src/xwalk/runtime/renderer/xwalk_content_renderer_client.cc +++ b/src/xwalk/runtime/renderer/xwalk_content_renderer_client.cc @@ -5,9 +5,12 @@ #include "xwalk/runtime/renderer/xwalk_content_renderer_client.h" #include "base/strings/utf_string_conversions.h" +#include "components/visitedlink/renderer/visitedlink_slave.h" #include "content/public/renderer/render_thread.h" +#include "grit/xwalk_application_resources.h" #include "grit/xwalk_sysapps_resources.h" #include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebSecurityPolicy.h" #include "xwalk/application/common/constants.h" #include "xwalk/application/renderer/application_native_module.h" @@ -17,6 +20,8 @@ #include "xwalk/runtime/renderer/android/xwalk_permission_client.h" #include "xwalk/runtime/renderer/android/xwalk_render_process_observer.h" #include "xwalk/runtime/renderer/android/xwalk_render_view_ext.h" +#else +#include "third_party/WebKit/public/web/WebFrame.h" #endif #if defined(OS_TIZEN_MOBILE) @@ -51,10 +56,12 @@ void XWalkContentRendererClient::RenderThreadStarted() { blink::WebSecurityPolicy::registerURLSchemeAsSecure(application_scheme); blink::WebSecurityPolicy::registerURLSchemeAsCORSEnabled(application_scheme); -#if defined(OS_ANDROID) content::RenderThread* thread = content::RenderThread::Get(); xwalk_render_process_observer_.reset(new XWalkRenderProcessObserver); thread->AddObserver(xwalk_render_process_observer_.get()); +#if defined(OS_ANDROID) + visited_link_slave_.reset(new visitedlink::VisitedLinkSlave); + thread->AddObserver(visited_link_slave_.get()); #endif } @@ -76,6 +83,10 @@ void XWalkContentRendererClient::DidCreateScriptContext( blink::WebFrame* frame, v8::Handle context, int extension_group, int world_id) { extension_controller_->DidCreateScriptContext(frame, context); +#if !defined(OS_ANDROID) + xwalk_render_process_observer_->DidCreateScriptContext( + frame, context, extension_group, world_id); +#endif } void XWalkContentRendererClient::WillReleaseScriptContext( @@ -93,6 +104,42 @@ void XWalkContentRendererClient::DidCreateModuleSystem( module_system->RegisterNativeModule("sysapps_promise", extensions::CreateJSModuleFromResource( IDR_XWALK_SYSAPPS_COMMON_PROMISE_API)); + module_system->RegisterNativeModule("widget_common", + extensions::CreateJSModuleFromResource( + IDR_XWALK_APPLICATION_WIDGET_COMMON_API)); +} + +#if defined(OS_ANDROID) +unsigned long long XWalkContentRendererClient::VisitedLinkHash( + const char* canonical_url, size_t length) { + return visited_link_slave_->ComputeURLFingerprint(canonical_url, length); } +bool XWalkContentRendererClient::IsLinkVisited(unsigned long long link_hash) { + return visited_link_slave_->IsVisited(link_hash); +} +#endif + +bool XWalkContentRendererClient::WillSendRequest(blink::WebFrame* frame, + content::PageTransition transition_type, + const GURL& url, + const GURL& first_party_for_cookies, + GURL* new_url) { +#if defined(OS_ANDROID) + return false; +#else + if (!xwalk_render_process_observer_->IsWarpMode()) + return false; + GURL origin_url(frame->document().url()); + if (origin_url.spec().empty() || + frame->document().securityOrigin().canRequest(url)) { + LOG(INFO) << "[PASS] " << origin_url.spec() << " request " << url.spec(); + return false; + } + + LOG(INFO) << "[BLOCK] " << origin_url.spec() << " request " << url.spec(); + *new_url = GURL(); + return true; +#endif +} } // namespace xwalk diff --git a/src/xwalk/runtime/renderer/xwalk_content_renderer_client.h b/src/xwalk/runtime/renderer/xwalk_content_renderer_client.h index 1fdb425..ee9852a 100644 --- a/src/xwalk/runtime/renderer/xwalk_content_renderer_client.h +++ b/src/xwalk/runtime/renderer/xwalk_content_renderer_client.h @@ -8,8 +8,18 @@ #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" #include "base/platform_file.h" +#include "content/public/common/page_transition_types.h" #include "content/public/renderer/content_renderer_client.h" #include "xwalk/extensions/renderer/xwalk_extension_renderer_controller.h" +#if defined(OS_ANDROID) +#include "xwalk/runtime/renderer/android/xwalk_render_process_observer.h" +#else +#include "xwalk/runtime/renderer/xwalk_render_process_observer_generic.h" +#endif + +namespace visitedlink { +class VisitedLinkSlave; +} namespace xwalk { @@ -36,6 +46,17 @@ class XWalkContentRendererClient virtual void WillReleaseScriptContext(blink::WebFrame* frame, v8::Handle, int world_id) OVERRIDE; +#if defined(OS_ANDROID) + virtual unsigned long long VisitedLinkHash(const char* canonical_url, + size_t length) OVERRIDE; + virtual bool IsLinkVisited(unsigned long long link_hash) OVERRIDE; +#endif + + virtual bool WillSendRequest(blink::WebFrame* frame, + content::PageTransition transition_type, + const GURL& url, + const GURL& first_party_for_cookies, + GURL* new_url) OVERRIDE; private: // XWalkExtensionRendererController::Delegate implementation. @@ -45,8 +66,9 @@ class XWalkContentRendererClient scoped_ptr extension_controller_; -#if defined(OS_ANDROID) scoped_ptr xwalk_render_process_observer_; +#if defined(OS_ANDROID) + scoped_ptr visited_link_slave_; #endif DISALLOW_COPY_AND_ASSIGN(XWalkContentRendererClient); diff --git a/src/xwalk/runtime/renderer/xwalk_render_process_observer_generic.cc b/src/xwalk/runtime/renderer/xwalk_render_process_observer_generic.cc new file mode 100644 index 0000000..14139d3 --- /dev/null +++ b/src/xwalk/runtime/renderer/xwalk_render_process_observer_generic.cc @@ -0,0 +1,95 @@ +// 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. + +#include "xwalk/runtime/renderer/xwalk_render_process_observer_generic.h" + +#include + +#include "ipc/ipc_message_macros.h" +#include "third_party/WebKit/public/web/WebSecurityPolicy.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "xwalk/runtime/common/xwalk_common_messages.h" + + +namespace xwalk { +namespace { +struct AccessWhitelistItem { + AccessWhitelistItem( + const GURL& source, const GURL& dest, bool allow_subdomains); + GURL source_; + GURL dest_; + bool allow_subdomains_; +}; + +AccessWhitelistItem::AccessWhitelistItem( + const GURL& source, const GURL& dest, bool allow_subdomains) + : source_(source), + dest_(dest), + allow_subdomains_(allow_subdomains) { +} + +std::vector access_whitelist; + +void AddAccessWhiteListEntry( + const GURL& source, const GURL& dest, bool allow_subdomains) { + blink::WebSecurityPolicy::addOriginAccessWhitelistEntry( + source.GetOrigin(), + blink::WebString::fromUTF8(dest.scheme()), + blink::WebString::fromUTF8(dest.HostNoBrackets()), + allow_subdomains); +} +} // namespace + +XWalkRenderProcessObserver::XWalkRenderProcessObserver() + : is_webkit_initialized_(false), + is_warp_mode_(false) { +} + +XWalkRenderProcessObserver::~XWalkRenderProcessObserver() { +} + +bool XWalkRenderProcessObserver::OnControlMessageReceived( + const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(XWalkRenderProcessObserver, message) + IPC_MESSAGE_HANDLER(ViewMsg_SetAccessWhiteList, OnSetAccessWhiteList) + IPC_MESSAGE_HANDLER(ViewMsg_EnableWarpMode, OnEnableWarpMode) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void XWalkRenderProcessObserver::WebKitInitialized() { + is_webkit_initialized_ = true; +} + +void XWalkRenderProcessObserver::OnRenderProcessShutdown() { + is_webkit_initialized_ = false; +} + +void XWalkRenderProcessObserver::DidCreateScriptContext( + blink::WebFrame* frame, v8::Handle context, + int extension_group, int world_id) { + for (std::vector::iterator it = access_whitelist.begin(); + it != access_whitelist.end(); ++it) + AddAccessWhiteListEntry(it->source_, it->dest_, it->allow_subdomains_); + + access_whitelist.clear(); +} + +void XWalkRenderProcessObserver::OnSetAccessWhiteList(const GURL& source, + const GURL& dest, + bool allow_subdomains) { + if (is_webkit_initialized_) + AddAccessWhiteListEntry(source, dest, allow_subdomains); + else + access_whitelist.push_back( + AccessWhitelistItem(source, dest, allow_subdomains)); +} + +void XWalkRenderProcessObserver::OnEnableWarpMode() { + is_warp_mode_ = true; +} + +} // namespace xwalk diff --git a/src/xwalk/runtime/renderer/xwalk_render_process_observer_generic.h b/src/xwalk/runtime/renderer/xwalk_render_process_observer_generic.h new file mode 100644 index 0000000..3592be0 --- /dev/null +++ b/src/xwalk/runtime/renderer/xwalk_render_process_observer_generic.h @@ -0,0 +1,51 @@ +// 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. + +#ifndef XWALK_RUNTIME_RENDERER_XWALK_RENDER_PROCESS_OBSERVER_GENERIC_H_ +#define XWALK_RUNTIME_RENDERER_XWALK_RENDER_PROCESS_OBSERVER_GENERIC_H_ + +#include + +#include "base/compiler_specific.h" +#include "content/public/renderer/render_process_observer.h" +#include "url/gurl.h" +#include "v8/include/v8.h" + +namespace blink { +class WebFrame; +} // namespace blink + +namespace xwalk { + +// FIXME: Using filename "xwalk_render_process_observer_generic.cc(h)" temporary +// , due to the conflict filename with Android port. +// A RenderViewObserver implementation used for handling XWalkView +// specific render-process wide IPC messages. +class XWalkRenderProcessObserver : public content::RenderProcessObserver { + public: + XWalkRenderProcessObserver(); + virtual ~XWalkRenderProcessObserver(); + + void DidCreateScriptContext( + blink::WebFrame* frame, v8::Handle context, + int extension_group, int world_id); + + // content::RenderProcessObserver implementation. + virtual bool OnControlMessageReceived(const IPC::Message& message) OVERRIDE; + virtual void WebKitInitialized() OVERRIDE; + virtual void OnRenderProcessShutdown() OVERRIDE; + + bool IsWarpMode() const { return is_warp_mode_; } + + private: + void OnSetAccessWhiteList( + const GURL& source, const GURL& dest, bool allow_subdomains); + void OnEnableWarpMode(); + + bool is_webkit_initialized_; + bool is_warp_mode_; +}; +} // namespace xwalk + +#endif // XWALK_RUNTIME_RENDERER_XWALK_RENDER_PROCESS_OBSERVER_GENERIC_H_ diff --git a/src/xwalk/sysapps/device_capabilities/device_capabilities.idl b/src/xwalk/sysapps/device_capabilities/device_capabilities.idl index 3b6e29a..58c4775 100644 --- a/src/xwalk/sysapps/device_capabilities/device_capabilities.idl +++ b/src/xwalk/sysapps/device_capabilities/device_capabilities.idl @@ -28,14 +28,16 @@ namespace device_capabilities { dictionary DisplayUnit { DOMString id; DOMString name; - boolean isPrimary; - boolean isInternal; - long dpiX; - long dpiY; + boolean primary; + boolean external; + long deviceXDPI; + long deviceYDPI; long width; long height; long availWidth; long availHeight; + long colorDepth; + long pixelDepth; }; dictionary SystemDisplay { diff --git a/src/xwalk/sysapps/device_capabilities/device_capabilities_api_browsertest.html b/src/xwalk/sysapps/device_capabilities/device_capabilities_api_browsertest.html index db66052..335f94f 100644 --- a/src/xwalk/sysapps/device_capabilities/device_capabilities_api_browsertest.html +++ b/src/xwalk/sysapps/device_capabilities/device_capabilities_api_browsertest.html @@ -109,17 +109,17 @@ if (typeof info.displays[i].name != "string") reportFail("Display name is not a string."); - if (typeof info.displays[i].isPrimary != "boolean") + if (typeof info.displays[i].primary != "boolean") reportFail("Display type is not a boolean."); - if (typeof info.displays[i].isInternal != "boolean") + if (typeof info.displays[i].external != "boolean") reportFail("Display type is not a boolean."); - if (typeof info.displays[i].dpiX != "number") - reportFail("Display dpiX must be a number."); + if (typeof info.displays[i].deviceXDPI != "number") + reportFail("Display deviceXDPI must be a number."); - if (typeof info.displays[i].dpiY != "number") - reportFail("Display dpiY must be a number."); + if (typeof info.displays[i].deviceYDPI != "number") + reportFail("Display deviceYDPI must be a number."); if (typeof info.displays[i].width != "number") reportFail("Display width must be a number."); @@ -132,6 +132,12 @@ if (typeof info.displays[i].availHeight != "number") reportFail("Display availHeight must be a number."); + + if (typeof info.displays[i].colorDepth != "number") + reportFail("Display colorDepth must be a number."); + + if (typeof info.displays[i].pixelDepth != "number") + reportFail("Display pixelDepth must be a number."); } if (document.title != "Fail") diff --git a/src/xwalk/sysapps/device_capabilities/display_info_provider.cc b/src/xwalk/sysapps/device_capabilities/display_info_provider.cc index d657218..5363de8 100644 --- a/src/xwalk/sysapps/device_capabilities/display_info_provider.cc +++ b/src/xwalk/sysapps/device_capabilities/display_info_provider.cc @@ -29,18 +29,23 @@ linked_ptr makeDisplayUnit(const gfx::Display& display) { // FIXME(YuZhiqiangX): find which field reflects 'name'. display_unit->name = "unknown"; - display_unit->is_primary = (display.id() == primary_display_id); - display_unit->is_internal = display.IsInternal(); + display_unit->primary = (display.id() == primary_display_id); + display_unit->external = !display.IsInternal(); const float dpi = display.device_scale_factor() * kDpi96; - display_unit->dpi_x = static_cast(dpi); - display_unit->dpi_y = static_cast(dpi); + display_unit->device_xdpi = static_cast(dpi); + display_unit->device_ydpi = static_cast(dpi); display_unit->width = display.bounds().width(); display_unit->height = display.bounds().height(); display_unit->avail_width = display.work_area_size().width(); display_unit->avail_height = display.work_area_size().height(); + // colorDepth and pixelDepth are constantly set as 24 refer to + // http://www.w3.org/TR/cssom-view/#dom-screen-colordepth + display_unit->color_depth = 24; + display_unit->pixel_depth = 24; + return display_unit; } diff --git a/src/xwalk/sysapps/raw_socket/raw_socket.idl b/src/xwalk/sysapps/raw_socket/raw_socket.idl index 1dde5ff..65dca0b 100644 --- a/src/xwalk/sysapps/raw_socket/raw_socket.idl +++ b/src/xwalk/sysapps/raw_socket/raw_socket.idl @@ -47,8 +47,22 @@ namespace raw_socket { ReadyState readyState; }; + // Events and functions are defined at + // udp_socket.idl + dictionary UDPSocket { + DOMString localAddress; + long localPort; + DOMString remoteAddress; + long remotePort; + boolean addressReuse; + boolean loopback; + long bufferedAmount; + ReadyState readyState; + }; + interface Functions { [nodoc] static TCPSocket TCPSocketConstructor(DOMString objectId); [nodoc] static TCPServerSocket TCPServerSocketConstructor(DOMString objectId); + [nodoc] static UDPSocket UDPSocketConstructor(DOMString objectId); }; }; diff --git a/src/xwalk/sysapps/raw_socket/raw_socket_api.js b/src/xwalk/sysapps/raw_socket/raw_socket_api.js index 8086be7..510c0ac 100644 --- a/src/xwalk/sysapps/raw_socket/raw_socket_api.js +++ b/src/xwalk/sysapps/raw_socket/raw_socket_api.js @@ -270,6 +270,138 @@ var TCPServerSocket = function(options) { TCPServerSocket.prototype = new common.EventTargetPrototype(); TCPServerSocket.prototype.constructor = TCPServerSocket; +// UDPSocket interface. +// +// TODO(tmpsantos): We are currently not throwing any exceptions +// neither validating the input parameters. +// +var UDPSocket = function(options) { + common.BindingObject.call(this, common.getUniqueId()); + common.EventTarget.call(this); + + internal.postMessage("UDPSocketConstructor", [this._id]); + + var options = options || {}; + + if (!options.localAddress) + options.localAddress = ""; + if (!options.localPort) + options.localPort = 0; + if (!options.remoteAddress) + options.remoteAddress = ""; + if (!options.remotePort) + options.remotePort = 0; + if (!options.addressReuse) + options.addressReuse = true; + if (!options.loopback) + options.loopback = false; + + this._addMethod("_close"); + this._addMethod("suspend"); + this._addMethod("resume"); + this._addMethod("joinMulticast"); + this._addMethod("leaveMulticast"); + this._addMethod("_sendString"); + + function MessageEvent(type, data) { + this.type = type; + this.data = data.data; + this.remotePort = data.remotePort; + this.remoteAddress = data.remoteAddress; + } + + this._addEvent("open"); + this._addEvent("drain"); + this._addEvent("error"); + this._addEvent("message", MessageEvent); + + function sendWrapper(data, remoteAddress, remotePort) { + this._sendString(data, remoteAddress, remotePort); + + // FIXME(tmpsantos): The spec says that send() should always + // return if you can keep sending data. This can only be + // verified in the native implementation, which makes this + // call sync. We are returning always true here to keep the + // implementation async. + return true; + }; + + function closeWrapper(data) { + if (this._readyStateObserver.readyState == "closed") + return; + + this._readyStateObserver.readyState = "closing"; + this._close(); + }; + + Object.defineProperties(this, { + "_readyStateObserver": { + value: new ReadyStateObserver(this._id, "opening"), + }, + "_readyStateObserverDeleter": { + value: v8tools.lifecycleTracker(), + }, + "send": { + value: sendWrapper, + enumerable: true, + }, + "close": { + value: closeWrapper, + enumerable: true, + }, + "remoteAddress": { + value: options.remoteAddress, + enumerable: true, + }, + "remotePort": { + value: options.remotePort, + enumerable: true, + }, + "localAddress": { + value: options.localAddress, + enumerable: true, + }, + "localPort": { + value: options.localPort, + enumerable: true, + }, + "addressReuse": { + value: options.addressReuse, + enumerable: true, + }, + "loopback": { + value: options.loopback, + enumerable: true, + }, + "bufferedAmount": { + value: 0, + enumerable: true, + }, + "readyState": { + get: function() { return this._readyStateObserver.readyState; }, + enumerable: true, + }, + }); + + var watcher = this._readyStateObserver; + this._readyStateObserverDeleter.destructor = function() { + watcher.destructor(); + }; + + // This is needed, otherwise events like "error" can get fired before + // we give the user a chance to register a listener. + function delayedInitialization(obj) { + obj._postMessage("init", [options]); + }; + + this._registerLifecycleTracker(); + setTimeout(delayedInitialization, 0, this); +}; + +UDPSocket.prototype = new common.EventTargetPrototype(); +UDPSocket.prototype.constructor = UDPSocket; + // Exported API. exports.TCPSocket = TCPSocket; exports.TCPServerSocket = TCPServerSocket; +exports.UDPSocket = UDPSocket; diff --git a/src/xwalk/sysapps/raw_socket/raw_socket_api_browsertest.html b/src/xwalk/sysapps/raw_socket/raw_socket_api_browsertest.html index 2d1a871..f2a6a94 100644 --- a/src/xwalk/sysapps/raw_socket/raw_socket_api_browsertest.html +++ b/src/xwalk/sysapps/raw_socket/raw_socket_api_browsertest.html @@ -10,8 +10,10 @@ var current_test = 0; var test_list = [ memoryManagement, - pingPong, - serverPortBusy, + pingPongTCP, + pingPongUDP, + serverPortBusyTCP, + serverPortBusyUDP, endTest ]; @@ -40,9 +42,16 @@ // initialization is completed, the object will be collected if no // variable is keeping a reference to it. function runGCandCheck() { + // The GC is called here more than once to make sure + // all the dangling objects get collected. Calling the GC + // one time does not give a guarantee that everything that + // can be collected will be collected. gc(); - if (garbageCollectionCount != 2) - reportFail("TCPSocket or TCPServerSocket is leaking."); + gc(); + gc(); + + if (garbageCollectionCount != 3) + reportFail("TCPSocket, TCPServerSocket or UDPSocket is leaking."); else runNextTest(); }; @@ -60,7 +69,7 @@ server.onopen = null; server = null; - if (++eventCount == 2) + if (++eventCount == 3) setTimeout(runGCandCheck, 0); }; @@ -76,7 +85,23 @@ client.onopen = null; client = null; - if (++eventCount == 2) + if (++eventCount == 3) + setTimeout(runGCandCheck, 0); + }; + + var udp = new api.UDPSocket({localAddress: "127.0.0.1", localPort: 7000}); + udp.onerror = udp.onopen = nullifyUDP; + udp.tracker = v8tools.lifecycleTracker(); + udp.tracker.destructor = function() { + garbageCollectionCount++; + }; + + function nullifyUDP() { + udp.onerror = null; + udp.onopen = null; + udp = null; + + if (++eventCount == 3) setTimeout(runGCandCheck, 0); }; }; @@ -89,7 +114,7 @@ // // This is not an API conformance test, it just verifies if the basic // functionality works. W3C should provide the former. - function pingPong(serverPort) { + function pingPongTCP(serverPort) { serverPort = serverPort || 5000; var serverPortMax = 5020; var testData = "Hello World!"; @@ -101,7 +126,7 @@ // The default port might be busy, so we try different ports // before reporting failure. if (serverPort < serverPortMax) - pingPong(++serverPort); + pingPongTCP(++serverPort); else reportFail("Not able to listen at port " + serverPort + "."); }; @@ -138,22 +163,72 @@ }; }; - function serverPortBusy(serverPort) { + function pingPongUDP(serverPort) { + serverPort = serverPort || 6000; + var serverPortMax = 6020; + var testData = "Hello World!"; + + var server = new api.UDPSocket( + {"localAddress": "127.0.0.1", "localPort": serverPort}); + + server.onerror = function() { + // The default port might be busy, so we try different ports + // before reporting failure. + if (serverPort < serverPortMax) + pingPongUDP(++serverPort); + else + reportFail("Not able to listen at port " + serverPort + "."); + }; + + server.onopen = function() { + var client = new api.UDPSocket( + {remoteAddress: "127.0.0.1", remotePort: serverPort}); + client.onopen = function() { + client.send(testData); + }; + + client.onerror = function() { + reportFail("Not able to connect to port " + serverPort + "."); + }; + + client.onmessage = function(event) { + var view = new Uint8Array(event.data); + var data = String.fromCharCode.apply(null, view); + + if (data != testData) + reportFail("Invalid data received by the client socket."); + else + runNextTest(); + }; + }; + + server.onmessage = function(event) { + var view = new Uint8Array(event.data); + var data = String.fromCharCode.apply(null, view); + + if (data != testData) + reportFail("Invalid data received by server socket."); + else + server.send(data, event.remoteAddress, event.remotePort); + }; + }; + + function serverPortBusy(Socket, serverPort) { serverPort = serverPort || 7000; var serverPortMax = 7020; - var server = new api.TCPServerSocket( + var server = new Socket( {"localAddress": "127.0.0.1", "localPort": serverPort}); server.onerror = function() { if (serverPort < serverPortMax) - serverPortBusy(++serverPort); + serverPortBusy(Socket, ++serverPort); else reportFail("Not able to listen at port " + serverPort + "."); }; server.onopen = function() { - var serverShouldFail = new api.TCPServerSocket( + var serverShouldFail = new Socket( {"localAddress": "127.0.0.1", "localPort": serverPort}); // Should fail, port already in use by another server. @@ -164,6 +239,14 @@ }; }; + function serverPortBusyTCP() { + serverPortBusy(api.TCPServerSocket); + }; + + function serverPortBusyUDP() { + serverPortBusy(api.UDPSocket); + }; + runNextTest(); diff --git a/src/xwalk/sysapps/raw_socket/raw_socket_extension.cc b/src/xwalk/sysapps/raw_socket/raw_socket_extension.cc index fe03277..6235422 100644 --- a/src/xwalk/sysapps/raw_socket/raw_socket_extension.cc +++ b/src/xwalk/sysapps/raw_socket/raw_socket_extension.cc @@ -9,6 +9,7 @@ #include "xwalk/sysapps/raw_socket/raw_socket.h" #include "xwalk/sysapps/raw_socket/tcp_server_socket_object.h" #include "xwalk/sysapps/raw_socket/tcp_socket_object.h" +#include "xwalk/sysapps/raw_socket/udp_socket_object.h" using namespace xwalk::jsapi::raw_socket; // NOLINT @@ -36,6 +37,9 @@ RawSocketInstance::RawSocketInstance() handler_.Register("TCPSocketConstructor", base::Bind(&RawSocketInstance::OnTCPSocketConstructor, base::Unretained(this))); + handler_.Register("UDPSocketConstructor", + base::Bind(&RawSocketInstance::OnUDPSocketConstructor, + base::Unretained(this))); } void RawSocketInstance::HandleMessage(scoped_ptr msg) { @@ -75,5 +79,19 @@ void RawSocketInstance::OnTCPSocketConstructor( store_.AddBindingObject(params->object_id, obj.Pass()); } +void RawSocketInstance::OnUDPSocketConstructor( + scoped_ptr info) { + scoped_ptr + params(UDPSocketConstructor::Params::Create(*info->arguments())); + + if (!params) { + LOG(WARNING) << "Malformed parameters passed to " << info->name(); + return; + } + + scoped_ptr obj(new UDPSocketObject); + store_.AddBindingObject(params->object_id, obj.Pass()); +} + } // namespace sysapps } // namespace xwalk diff --git a/src/xwalk/sysapps/raw_socket/raw_socket_extension.h b/src/xwalk/sysapps/raw_socket/raw_socket_extension.h index 25fedc4..499c54b 100644 --- a/src/xwalk/sysapps/raw_socket/raw_socket_extension.h +++ b/src/xwalk/sysapps/raw_socket/raw_socket_extension.h @@ -40,6 +40,7 @@ class RawSocketInstance : public XWalkExtensionInstance { void OnTCPServerSocketConstructor( scoped_ptr info); void OnTCPSocketConstructor(scoped_ptr info); + void OnUDPSocketConstructor(scoped_ptr info); XWalkExtensionFunctionHandler handler_; BindingObjectStore store_; diff --git a/src/xwalk/sysapps/raw_socket/udp_socket.idl b/src/xwalk/sysapps/raw_socket/udp_socket.idl new file mode 100644 index 0000000..a4c7101 --- /dev/null +++ b/src/xwalk/sysapps/raw_socket/udp_socket.idl @@ -0,0 +1,49 @@ +// Copyright (c) 2013 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// RawSocket API - UDPSocket +namespace udp_socket { + dictionary UDPMessageEvent { + ArrayBuffer data; + DOMString remoteAddress; + long remotePort; + }; + + dictionary UDPOptions { + DOMString localAddress; + long localPort; + DOMString remoteAddress; + long remotePort; + boolean addressReuse; + boolean loopback; + }; + + interface Events { + static void ondrain(); + static void onopen(); + static void onerror(); + static void onmessage(); + }; + + interface Functions { + static void close(); + static void suspend(); + static void resume(); + static void joinMulticast(DOMString multicastGroupAddress); + static void leaveMulticast(DOMString multicastGroupAddress); + + [nocompile] static boolean send(object data, optional DOMString remoteAddress, + optional long remotePort); + + // send() can take up to four different types of arguments. We try to + // detect what kind of argument we have and route to a more specialized + // handler. + + [nodoc] static boolean sendDOMString(DOMString data, + optional DOMString remoteAddress, optional long remotePort); + + [nodoc] static void init(optional UDPOptions options); + [nodoc] static void destroy(); + }; +}; diff --git a/src/xwalk/sysapps/raw_socket/udp_socket_object.cc b/src/xwalk/sysapps/raw_socket/udp_socket_object.cc new file mode 100644 index 0000000..bcbeb6f --- /dev/null +++ b/src/xwalk/sysapps/raw_socket/udp_socket_object.cc @@ -0,0 +1,276 @@ +// Copyright (c) 2013 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "xwalk/sysapps/raw_socket/udp_socket_object.h" + +#include + +#include "base/logging.h" +#include "net/base/net_errors.h" +#include "xwalk/sysapps/raw_socket/udp_socket.h" + +using namespace xwalk::jsapi::udp_socket; // NOLINT +using namespace xwalk::jsapi::raw_socket; // NOLINT + +namespace { + +const unsigned kBufferSize = 4096; + +} // namespace + +namespace xwalk { +namespace sysapps { + +UDPSocketObject::UDPSocketObject() + : has_write_pending_(false), + is_suspended_(false), + is_reading_(false), + resolver_(net::HostResolver::CreateDefaultResolver(NULL)), + read_buffer_(new net::IOBuffer(kBufferSize)), + write_buffer_(new net::IOBuffer(kBufferSize)), + single_resolver_(new net::SingleRequestHostResolver(resolver_.get())) { + handler_.Register("init", + base::Bind(&UDPSocketObject::OnInit, base::Unretained(this))); + handler_.Register("_close", + base::Bind(&UDPSocketObject::OnClose, base::Unretained(this))); + handler_.Register("suspend", + base::Bind(&UDPSocketObject::OnSuspend, base::Unretained(this))); + handler_.Register("resume", + base::Bind(&UDPSocketObject::OnResume, base::Unretained(this))); + handler_.Register("joinMulticast", + base::Bind(&UDPSocketObject::OnJoinMulticast, base::Unretained(this))); + handler_.Register("leaveMulticast", + base::Bind(&UDPSocketObject::OnLeaveMulticast, base::Unretained(this))); + handler_.Register("_sendString", + base::Bind(&UDPSocketObject::OnSendString, base::Unretained(this))); +} + +UDPSocketObject::~UDPSocketObject() {} + +void UDPSocketObject::DoRead() { + if (!socket_->is_connected()) + return; + + is_reading_ = true; + + int ret = socket_->RecvFrom(read_buffer_, + kBufferSize, + &from_, + base::Bind(&UDPSocketObject::OnRead, + base::Unretained(this))); + + if (ret > 0) + OnRead(ret); +} + +void UDPSocketObject::OnInit(scoped_ptr info) { + scoped_ptr params(Init::Params::Create(*info->arguments())); + if (!params) { + LOG(WARNING) << "Malformed parameters passed to " << info->name(); + setReadyState(READY_STATE_CLOSED); + DispatchEvent("error"); + return; + } + + socket_.reset(new net::UDPSocket(net::DatagramSocket::DEFAULT_BIND, + net::RandIntCallback(), + NULL, + net::NetLog::Source())); + + if (!params->options) { + OnConnectionOpen(net::OK); + return; + } + + if (!params->options->local_address.empty()) { + net::IPAddressNumber ip_number; + if (!net::ParseIPLiteralToNumber(params->options->local_address, + &ip_number)) { + LOG(WARNING) << "Invalid IP address " << params->options->local_address; + setReadyState(READY_STATE_CLOSED); + DispatchEvent("error"); + return; + } + + if (params->options->address_reuse) + socket_->AllowAddressReuse(); + + net::IPEndPoint end_point(ip_number, params->options->local_port); + if (socket_->Bind(end_point) != net::OK) { + LOG(WARNING) << "Can't bind to " << end_point.ToString(); + setReadyState(READY_STATE_CLOSED); + DispatchEvent("error"); + return; + } + + DoRead(); + OnConnectionOpen(net::OK); + return; + } + + if (params->options->remote_address.empty() || + !params->options->remote_port) { + OnConnectionOpen(net::OK); + return; + } + + net::HostResolver::RequestInfo request_info(net::HostPortPair( + params->options->remote_address, params->options->remote_port)); + + int ret = single_resolver_->Resolve( + request_info, + net::DEFAULT_PRIORITY, + &addresses_, + base::Bind(&UDPSocketObject::OnConnectionOpen, + base::Unretained(this)), + net::BoundNetLog()); + + if (ret != net::ERR_IO_PENDING) + OnConnectionOpen(ret); +} + +void UDPSocketObject::OnClose(scoped_ptr info) { + socket_.reset(); +} + +void UDPSocketObject::OnSuspend(scoped_ptr info) { + is_suspended_ = true; +} + +void UDPSocketObject::OnResume(scoped_ptr info) { + is_suspended_ = false; +} + +void UDPSocketObject::OnJoinMulticast( + scoped_ptr info) { +} + +void UDPSocketObject::OnLeaveMulticast( + scoped_ptr info) { +} + +void UDPSocketObject::OnSendString( + scoped_ptr info) { + if (!socket_ || has_write_pending_) + return; + + scoped_ptr + params(SendDOMString::Params::Create(*info->arguments())); + if (!params) { + LOG(WARNING) << "Malformed parameters passed to " << info->name(); + return; + } + + if (params->data.size() > kBufferSize) { + LOG(WARNING) << "Write data bigger than the write buffer."; + return; + } + + write_buffer_size_ = params->data.size(); + memcpy(write_buffer_->data(), params->data.data(), write_buffer_size_); + + if (!params->remote_address || !*params->remote_port) { + OnSend(net::OK); + return; + } + + net::HostResolver::RequestInfo request_info(net::HostPortPair( + *params->remote_address, *params->remote_port)); + + int ret = single_resolver_->Resolve( + request_info, + net::DEFAULT_PRIORITY, + &addresses_, + base::Bind(&UDPSocketObject::OnSend, + base::Unretained(this)), + net::BoundNetLog()); + + if (ret != net::ERR_IO_PENDING) + OnSend(ret); + else + has_write_pending_ = true; +} + +void UDPSocketObject::OnRead(int status) { + // No data means the other side has + // disconnected the socket. + if (status == 0) { + setReadyState(READY_STATE_CLOSED); + DispatchEvent("close"); + return; + } + + scoped_ptr data(base::BinaryValue::CreateWithCopiedBuffer( + static_cast(read_buffer_->data()), status)); + + UDPMessageEvent event; + event.data = std::string(read_buffer_->data(), status); + event.remote_port = from_.port(); + event.remote_address = from_.ToStringWithoutPort(); + + scoped_ptr eventData(new base::ListValue); + eventData->Append(event.ToValue().release()); + + if (!is_suspended_) + DispatchEvent("message", eventData.Pass()); + + DoRead(); +} + +void UDPSocketObject::OnWrite(int status) { + has_write_pending_ = false; + DispatchEvent("drain"); +} + +void UDPSocketObject::OnConnectionOpen(int status) { + if (status != net::OK) { + setReadyState(READY_STATE_CLOSED); + DispatchEvent("error"); + return; + } + + setReadyState(READY_STATE_OPEN); + DispatchEvent("open"); +} + +void UDPSocketObject::OnSend(int status) { + if (status != net::OK || addresses_.empty()) { + setReadyState(READY_STATE_CLOSED); + DispatchEvent("error"); + return; + } + + if (!socket_->is_connected()) { + // If we are waiting for reads and the socket is not connect, + // it means the connection was closed. + if (is_reading_ || socket_->Connect(addresses_[0]) != net::OK) { + setReadyState(READY_STATE_CLOSED); + DispatchEvent("error"); + return; + } + } + + int ret = socket_->SendTo( + write_buffer_, + write_buffer_size_, + addresses_[0], + base::Bind(&UDPSocketObject::OnWrite, base::Unretained(this))); + + if (ret == net::ERR_IO_PENDING) { + has_write_pending_ = true; + } else if (ret == write_buffer_size_) { + has_write_pending_ = false; + } else { + socket_->Close(); + setReadyState(READY_STATE_CLOSED); + DispatchEvent("close"); + return; + } + + if (!is_reading_ && socket_->is_connected()) + DoRead(); +} + +} // namespace sysapps +} // namespace xwalk diff --git a/src/xwalk/sysapps/raw_socket/udp_socket_object.h b/src/xwalk/sysapps/raw_socket/udp_socket_object.h new file mode 100644 index 0000000..ca40741 --- /dev/null +++ b/src/xwalk/sysapps/raw_socket/udp_socket_object.h @@ -0,0 +1,63 @@ +// Copyright (c) 2013 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef XWALK_SYSAPPS_RAW_SOCKET_UDP_SOCKET_OBJECT_H_ +#define XWALK_SYSAPPS_RAW_SOCKET_UDP_SOCKET_OBJECT_H_ + +#include + +#include "net/base/address_list.h" +#include "net/base/io_buffer.h" +#include "net/dns/single_request_host_resolver.h" +#include "net/udp/udp_socket.h" +#include "xwalk/sysapps/raw_socket/raw_socket_object.h" + +namespace xwalk { +namespace sysapps { + +class UDPSocketObject : public RawSocketObject { + public: + UDPSocketObject(); + virtual ~UDPSocketObject(); + + private: + void DoRead(); + + // JavaScript function handlers. + void OnInit(scoped_ptr info); + void OnClose(scoped_ptr info); + void OnSuspend(scoped_ptr info); + void OnResume(scoped_ptr info); + void OnJoinMulticast(scoped_ptr info); + void OnLeaveMulticast(scoped_ptr info); + void OnSendString(scoped_ptr info); + + // net::UDPSocket callbacks. + void OnRead(int status); + void OnWrite(int status); + + // net::SingleRequestHostResolver callbacks. + void OnConnectionOpen(int status); + void OnSend(int status); + + bool has_write_pending_; + bool is_suspended_; + bool is_reading_; + + scoped_refptr read_buffer_; + scoped_refptr write_buffer_; + scoped_ptr socket_; + + unsigned write_buffer_size_; + + scoped_ptr resolver_; + scoped_ptr single_resolver_; + net::AddressList addresses_; + net::IPEndPoint from_; +}; + +} // namespace sysapps +} // namespace xwalk + +#endif // XWALK_SYSAPPS_RAW_SOCKET_UDP_SOCKET_OBJECT_H_ diff --git a/src/xwalk/sysapps/sysapps.gyp b/src/xwalk/sysapps/sysapps.gyp index cdc0aa7..848a11f 100644 --- a/src/xwalk/sysapps/sysapps.gyp +++ b/src/xwalk/sysapps/sysapps.gyp @@ -66,6 +66,9 @@ 'raw_socket/tcp_socket.idl', 'raw_socket/tcp_socket_object.cc', 'raw_socket/tcp_socket_object.h', + 'raw_socket/udp_socket.idl', + 'raw_socket/udp_socket_object.cc', + 'raw_socket/udp_socket_object.h', ], 'conditions': [ ['OS!="android"', { diff --git a/src/xwalk/test/android/core/javatests/src/org/xwalk/core/xwview/test/NullContentsClient.java b/src/xwalk/test/android/core/javatests/src/org/xwalk/core/xwview/test/NullContentsClient.java index 80a210d..f64fc93 100644 --- a/src/xwalk/test/android/core/javatests/src/org/xwalk/core/xwview/test/NullContentsClient.java +++ b/src/xwalk/test/android/core/javatests/src/org/xwalk/core/xwview/test/NullContentsClient.java @@ -16,7 +16,6 @@ import android.webkit.WebResourceResponse; import org.xwalk.core.JsPromptResult; import org.xwalk.core.JsResult; -import org.xwalk.core.SslErrorHandler; import org.xwalk.core.XWalkContentsClient; import org.xwalk.core.XWalkGeolocationPermissions; import org.xwalk.core.XWalkHttpAuthHandler; @@ -58,7 +57,7 @@ public class NullContentsClient extends XWalkContentsClient { } @Override - public void onReceivedSslError(SslErrorHandler handler, SslError error) { + public void onReceivedSslError(ValueCallback callback, SslError error) { } @Override diff --git a/src/xwalk/xwalk.gyp b/src/xwalk/xwalk.gyp index b398009..f739644 100644 --- a/src/xwalk/xwalk.gyp +++ b/src/xwalk/xwalk.gyp @@ -28,6 +28,8 @@ '../base/base.gyp:base', '../base/base.gyp:base_i18n', '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', + '../components/components.gyp:visitedlink_browser', + '../components/components.gyp:visitedlink_renderer', '../content/content.gyp:content', '../content/content.gyp:content_app_both', '../content/content.gyp:content_browser', @@ -217,6 +219,10 @@ 'runtime/common/android/xwalk_render_view_messages.h', 'runtime/common/paths_mac.h', 'runtime/common/paths_mac.mm', + 'runtime/common/xwalk_common_messages.cc', + 'runtime/common/xwalk_common_messages.h', + 'runtime/common/xwalk_common_message_generator.cc', + 'runtime/common/xwalk_common_message_generator.h', 'runtime/common/xwalk_content_client.cc', 'runtime/common/xwalk_content_client.h', 'runtime/common/xwalk_paths.cc', @@ -235,6 +241,8 @@ 'runtime/renderer/tizen/xwalk_content_renderer_client_tizen.h', 'runtime/renderer/xwalk_content_renderer_client.cc', 'runtime/renderer/xwalk_content_renderer_client.h', + 'runtime/renderer/xwalk_render_process_observer_generic.cc', + 'runtime/renderer/xwalk_render_process_observer_generic.h', ], 'includes': [ 'xwalk_jsapi.gypi', @@ -262,6 +270,10 @@ 'xwalk_core_jar_jni', 'xwalk_core_native_jni', ], + 'sources!':[ + 'runtime/renderer/xwalk_render_process_observer_generic.cc', + 'runtime/renderer/xwalk_render_process_observer_generic.h', + ], }], ['OS=="win" and win_use_allocator_shim==1', { 'dependencies': [ diff --git a/src/xwalk/xwalk_tests.gypi b/src/xwalk/xwalk_tests.gypi index 173db0a..86d2680 100644 --- a/src/xwalk/xwalk_tests.gypi +++ b/src/xwalk/xwalk_tests.gypi @@ -43,6 +43,7 @@ 'application/common/manifest_handlers/main_document_handler_unittest.cc', 'application/common/manifest_handlers/permissions_handler_unittest.cc', 'application/common/manifest_handlers/warp_handler_unittest.cc', + 'application/common/manifest_handlers/widget_handler_unittest.cc', 'application/common/manifest_handler_unittest.cc', 'application/common/manifest_unittest.cc', 'runtime/common/xwalk_content_client_unittest.cc', @@ -57,6 +58,11 @@ '../skia/skia.gyp:skia', ], }], + ['tizen == 1 or tizen_mobile == 1', { + 'sources': [ + 'application/common/manifest_handlers/navigation_handler_unittest.cc', + ], + }], ], }, {