Extract SIGPIPE ignoring code to a common place.
[chromium-blink-merge.git] / content / renderer / render_widget_fullscreen_pepper.cc
blob58cb9dae1f98c667a979dddf0c1f5efaca13eaec
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/renderer/render_widget_fullscreen_pepper.h"
7 #include <vector>
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/message_loop.h"
12 #include "content/common/gpu/client/gpu_channel_host.h"
13 #include "content/common/view_messages.h"
14 #include "content/public/common/content_switches.h"
15 #include "content/renderer/pepper/pepper_platform_context_3d_impl.h"
16 #include "content/renderer/render_thread_impl.h"
17 #include "gpu/command_buffer/client/gles2_implementation.h"
18 #include "skia/ext/platform_canvas.h"
19 #include "third_party/WebKit/Source/Platform/chromium/public/WebGraphicsContext3D.h"
20 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebCanvas.h"
21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebCursorInfo.h"
22 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebSize.h"
23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebWidget.h"
24 #include "ui/gfx/size_conversions.h"
25 #include "ui/gl/gpu_preference.h"
26 #include "webkit/plugins/ppapi/plugin_delegate.h"
27 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
29 using WebKit::WebCanvas;
30 using WebKit::WebCompositionUnderline;
31 using WebKit::WebCursorInfo;
32 using WebKit::WebGestureEvent;
33 using WebKit::WebInputEvent;
34 using WebKit::WebMouseEvent;
35 using WebKit::WebMouseWheelEvent;
36 using WebKit::WebPoint;
37 using WebKit::WebRect;
38 using WebKit::WebSize;
39 using WebKit::WebString;
40 using WebKit::WebTextDirection;
41 using WebKit::WebTextInputType;
42 using WebKit::WebVector;
43 using WebKit::WebWidget;
44 using WebKit::WGC3Dintptr;
46 namespace content {
48 namespace {
50 // See third_party/WebKit/Source/WebCore/dom/WheelEvent.h.
51 const float kTickDivisor = 120.0f;
53 class FullscreenMouseLockDispatcher : public MouseLockDispatcher {
54 public:
55 explicit FullscreenMouseLockDispatcher(RenderWidgetFullscreenPepper* widget);
56 virtual ~FullscreenMouseLockDispatcher();
58 private:
59 // MouseLockDispatcher implementation.
60 virtual void SendLockMouseRequest(bool unlocked_by_target) OVERRIDE;
61 virtual void SendUnlockMouseRequest() OVERRIDE;
63 RenderWidgetFullscreenPepper* widget_;
65 DISALLOW_COPY_AND_ASSIGN(FullscreenMouseLockDispatcher);
68 FullscreenMouseLockDispatcher::FullscreenMouseLockDispatcher(
69 RenderWidgetFullscreenPepper* widget) : widget_(widget) {
72 FullscreenMouseLockDispatcher::~FullscreenMouseLockDispatcher() {
75 void FullscreenMouseLockDispatcher::SendLockMouseRequest(
76 bool unlocked_by_target) {
77 widget_->Send(new ViewHostMsg_LockMouse(widget_->routing_id(), false,
78 unlocked_by_target, true));
81 void FullscreenMouseLockDispatcher::SendUnlockMouseRequest() {
82 widget_->Send(new ViewHostMsg_UnlockMouse(widget_->routing_id()));
85 // WebWidget that simply wraps the pepper plugin.
86 class PepperWidget : public WebWidget {
87 public:
88 explicit PepperWidget(RenderWidgetFullscreenPepper* widget)
89 : widget_(widget) {
92 virtual ~PepperWidget() {}
94 // WebWidget API
95 virtual void close() {
96 delete this;
99 virtual WebSize size() {
100 return size_;
103 virtual void willStartLiveResize() {
106 virtual void resize(const WebSize& size) {
107 if (!widget_->plugin())
108 return;
110 size_ = size;
111 WebRect plugin_rect(0, 0, size_.width, size_.height);
112 widget_->plugin()->ViewChanged(plugin_rect, plugin_rect,
113 std::vector<gfx::Rect>());
114 widget_->Invalidate();
117 virtual void willEndLiveResize() {
120 virtual void animate(double frameBeginTime) {
123 virtual void layout() {
126 #if WEBWIDGET_HAS_PAINT_OPTIONS
127 virtual void paint(WebCanvas* canvas, const WebRect& rect, PaintOptions) {
128 #else
129 virtual void paint(WebCanvas* canvas, const WebRect& rect) {
130 #endif
131 if (!widget_->plugin())
132 return;
134 SkAutoCanvasRestore auto_restore(canvas, true);
135 float canvas_scale = widget_->deviceScaleFactor();
136 canvas->scale(canvas_scale, canvas_scale);
138 WebRect plugin_rect(0, 0, size_.width, size_.height);
139 widget_->plugin()->Paint(canvas, plugin_rect, rect);
142 #if WEBWIDGET_HAS_SETCOMPOSITORSURFACEREADY
143 virtual void setCompositorSurfaceReady() {
145 #endif
147 virtual void composite(bool finish) {
148 if (!widget_->plugin())
149 return;
151 WebGraphicsContext3DCommandBufferImpl* context = widget_->context();
152 DCHECK(context);
153 unsigned int texture = widget_->plugin()->GetBackingTextureId();
154 context->bindTexture(GL_TEXTURE_2D, texture);
155 context->drawArrays(GL_TRIANGLES, 0, 3);
156 widget_->SwapBuffers();
159 virtual void themeChanged() {
160 NOTIMPLEMENTED();
163 virtual bool handleInputEvent(const WebInputEvent& event) {
164 if (!widget_->plugin())
165 return false;
167 // This cursor info is ignored, we always set the cursor directly from
168 // RenderWidgetFullscreenPepper::DidChangeCursor.
169 WebCursorInfo cursor;
170 bool result = widget_->plugin()->HandleInputEvent(event, &cursor);
172 // For normal web pages, WebCore::EventHandler converts selected
173 // gesture events into mouse and wheel events. We don't have a WebView
174 // so do this translation here.
175 if (!result && WebInputEvent::isGestureEventType(event.type)) {
176 switch (event.type) {
177 case WebInputEvent::GestureScrollUpdate: {
178 const WebGestureEvent* gesture_event =
179 static_cast<const WebGestureEvent*>(&event);
180 WebMouseWheelEvent wheel_event;
181 wheel_event.timeStampSeconds = gesture_event->timeStampSeconds;
182 wheel_event.type = WebInputEvent::MouseWheel;
183 wheel_event.modifiers = gesture_event->modifiers;
185 wheel_event.x = gesture_event->x;
186 wheel_event.y = gesture_event->y;
187 wheel_event.windowX = gesture_event->globalX;
188 wheel_event.windowY = gesture_event->globalX;
189 wheel_event.globalX = gesture_event->globalX;
190 wheel_event.globalY = gesture_event->globalY;
191 wheel_event.movementX = 0;
192 wheel_event.movementY = 0;
194 wheel_event.deltaX = gesture_event->data.scrollUpdate.deltaX;
195 wheel_event.deltaY = gesture_event->data.scrollUpdate.deltaY;
196 wheel_event.wheelTicksX =
197 gesture_event->data.scrollUpdate.deltaX / kTickDivisor;
198 wheel_event.wheelTicksY =
199 gesture_event->data.scrollUpdate.deltaY / kTickDivisor;
200 wheel_event.hasPreciseScrollingDeltas = 1;
201 wheel_event.phase = WebMouseWheelEvent::PhaseNone;
202 wheel_event.momentumPhase = WebMouseWheelEvent::PhaseNone;
204 result |= widget_->plugin()->HandleInputEvent(wheel_event, &cursor);
205 break;
207 case WebInputEvent::GestureTap: {
208 const WebGestureEvent* gesture_event =
209 static_cast<const WebGestureEvent*>(&event);
210 WebMouseEvent mouseEvent;
212 mouseEvent.timeStampSeconds = gesture_event->timeStampSeconds;
213 mouseEvent.type = WebInputEvent::MouseMove;
214 mouseEvent.modifiers = gesture_event->modifiers;
216 mouseEvent.x = gesture_event->x;
217 mouseEvent.y = gesture_event->y;
218 mouseEvent.windowX = gesture_event->globalX;
219 mouseEvent.windowY = gesture_event->globalX;
220 mouseEvent.globalX = gesture_event->globalX;
221 mouseEvent.globalY = gesture_event->globalY;
222 mouseEvent.movementX = 0;
223 mouseEvent.movementY = 0;
224 result |= widget_->plugin()->HandleInputEvent(mouseEvent, &cursor);
226 mouseEvent.type = WebInputEvent::MouseDown;
227 mouseEvent.button = WebMouseEvent::ButtonLeft;
228 mouseEvent.clickCount = gesture_event->data.tap.tapCount;
229 result |= widget_->plugin()->HandleInputEvent(mouseEvent, &cursor);
231 mouseEvent.type = WebInputEvent::MouseUp;
232 result |= widget_->plugin()->HandleInputEvent(mouseEvent, &cursor);
233 break;
235 default:
236 break;
240 // For normal web pages, WebViewImpl does input event translations and
241 // generates context menu events. Since we don't have a WebView, we need to
242 // do the necessary translation ourselves.
243 if (WebInputEvent::isMouseEventType(event.type)) {
244 const WebMouseEvent& mouse_event =
245 reinterpret_cast<const WebMouseEvent&>(event);
246 bool send_context_menu_event = false;
247 // On Mac/Linux, we handle it on mouse down.
248 // On Windows, we handle it on mouse up.
249 #if defined(OS_WIN)
250 send_context_menu_event =
251 mouse_event.type == WebInputEvent::MouseUp &&
252 mouse_event.button == WebMouseEvent::ButtonRight;
253 #elif defined(OS_MACOSX)
254 send_context_menu_event =
255 mouse_event.type == WebInputEvent::MouseDown &&
256 (mouse_event.button == WebMouseEvent::ButtonRight ||
257 (mouse_event.button == WebMouseEvent::ButtonLeft &&
258 mouse_event.modifiers & WebMouseEvent::ControlKey));
259 #else
260 send_context_menu_event =
261 mouse_event.type == WebInputEvent::MouseDown &&
262 mouse_event.button == WebMouseEvent::ButtonRight;
263 #endif
264 if (send_context_menu_event) {
265 WebMouseEvent context_menu_event(mouse_event);
266 context_menu_event.type = WebInputEvent::ContextMenu;
267 widget_->plugin()->HandleInputEvent(context_menu_event, &cursor);
270 return result;
273 virtual void mouseCaptureLost() {
274 NOTIMPLEMENTED();
277 virtual void setFocus(bool focus) {
278 NOTIMPLEMENTED();
281 // TODO(piman): figure out IME and implement these if necessary.
282 virtual bool setComposition(
283 const WebString& text,
284 const WebVector<WebCompositionUnderline>& underlines,
285 int selectionStart,
286 int selectionEnd) {
287 return false;
290 virtual bool confirmComposition() {
291 return false;
294 virtual bool compositionRange(size_t* location, size_t* length) {
295 return false;
298 virtual bool confirmComposition(const WebString& text) {
299 return false;
302 virtual WebTextInputType textInputType() {
303 return WebKit::WebTextInputTypeNone;
306 virtual WebRect caretOrSelectionBounds() {
307 return WebRect();
310 virtual bool selectionRange(WebPoint& start, WebPoint& end) const {
311 return false;
314 virtual bool caretOrSelectionRange(size_t* location, size_t* length) {
315 return false;
318 virtual void setTextDirection(WebTextDirection) {
321 virtual bool isAcceleratedCompositingActive() const {
322 return widget_->context() && widget_->plugin() &&
323 (widget_->plugin()->GetBackingTextureId() != 0);
326 private:
327 RenderWidgetFullscreenPepper* widget_;
328 WebSize size_;
330 DISALLOW_COPY_AND_ASSIGN(PepperWidget);
333 void DestroyContext(WebGraphicsContext3DCommandBufferImpl* context,
334 GLuint program,
335 GLuint buffer) {
336 DCHECK(context);
337 if (program)
338 context->deleteProgram(program);
339 if (buffer)
340 context->deleteBuffer(buffer);
341 delete context;
344 } // anonymous namespace
346 // static
347 RenderWidgetFullscreenPepper* RenderWidgetFullscreenPepper::Create(
348 int32 opener_id, webkit::ppapi::PluginInstance* plugin,
349 const GURL& active_url,
350 const WebKit::WebScreenInfo& screen_info) {
351 DCHECK_NE(MSG_ROUTING_NONE, opener_id);
352 scoped_refptr<RenderWidgetFullscreenPepper> widget(
353 new RenderWidgetFullscreenPepper(plugin, active_url, screen_info));
354 widget->Init(opener_id);
355 return widget.release();
358 RenderWidgetFullscreenPepper::RenderWidgetFullscreenPepper(
359 webkit::ppapi::PluginInstance* plugin,
360 const GURL& active_url,
361 const WebKit::WebScreenInfo& screen_info)
362 : RenderWidgetFullscreen(screen_info),
363 active_url_(active_url),
364 plugin_(plugin),
365 context_(NULL),
366 buffer_(0),
367 program_(0),
368 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
369 mouse_lock_dispatcher_(new FullscreenMouseLockDispatcher(
370 ALLOW_THIS_IN_INITIALIZER_LIST(this))) {
373 RenderWidgetFullscreenPepper::~RenderWidgetFullscreenPepper() {
374 if (context_)
375 DestroyContext(context_, program_, buffer_);
378 void RenderWidgetFullscreenPepper::OnViewContextSwapBuffersPosted() {
379 OnSwapBuffersPosted();
382 void RenderWidgetFullscreenPepper::OnViewContextSwapBuffersComplete() {
383 OnSwapBuffersComplete();
386 void RenderWidgetFullscreenPepper::OnViewContextSwapBuffersAborted() {
387 if (!context_)
388 return;
389 // Destroy the context later, in case we got called from InitContext for
390 // example. We still need to reset context_ now so that a new context gets
391 // created when the plugin recreates its own.
392 MessageLoop::current()->PostTask(
393 FROM_HERE,
394 base::Bind(&DestroyContext, context_, program_, buffer_));
395 context_ = NULL;
396 program_ = 0;
397 buffer_ = 0;
398 OnSwapBuffersAborted();
399 CheckCompositing();
403 void RenderWidgetFullscreenPepper::Invalidate() {
404 InvalidateRect(gfx::Rect(size_.width(), size_.height()));
407 void RenderWidgetFullscreenPepper::InvalidateRect(const WebKit::WebRect& rect) {
408 if (CheckCompositing()) {
409 scheduleComposite();
410 } else {
411 didInvalidateRect(rect);
415 void RenderWidgetFullscreenPepper::ScrollRect(
416 int dx, int dy, const WebKit::WebRect& rect) {
417 if (CheckCompositing()) {
418 scheduleComposite();
419 } else {
420 didScrollRect(dx, dy, rect);
424 void RenderWidgetFullscreenPepper::Destroy() {
425 // This function is called by the plugin instance as it's going away, so reset
426 // plugin_ to NULL to avoid calling into a dangling pointer e.g. on Close().
427 plugin_ = NULL;
428 Send(new ViewHostMsg_Close(routing_id_));
429 Release();
432 void RenderWidgetFullscreenPepper::DidChangeCursor(
433 const WebKit::WebCursorInfo& cursor) {
434 didChangeCursor(cursor);
437 webkit::ppapi::PluginDelegate::PlatformContext3D*
438 RenderWidgetFullscreenPepper::CreateContext3D() {
439 #ifdef ENABLE_GPU
440 return new PlatformContext3DImpl(this);
441 #else
442 return NULL;
443 #endif
446 void RenderWidgetFullscreenPepper::ReparentContext(
447 webkit::ppapi::PluginDelegate::PlatformContext3D* context) {
448 static_cast<PlatformContext3DImpl*>(context)->SetParentContext(this);
451 bool RenderWidgetFullscreenPepper::OnMessageReceived(const IPC::Message& msg) {
452 bool handled = true;
453 IPC_BEGIN_MESSAGE_MAP(RenderWidgetFullscreenPepper, msg)
454 IPC_MESSAGE_FORWARD(ViewMsg_LockMouse_ACK,
455 mouse_lock_dispatcher_.get(),
456 MouseLockDispatcher::OnLockMouseACK)
457 IPC_MESSAGE_FORWARD(ViewMsg_MouseLockLost,
458 mouse_lock_dispatcher_.get(),
459 MouseLockDispatcher::OnMouseLockLost)
460 IPC_MESSAGE_UNHANDLED(handled = false)
461 IPC_END_MESSAGE_MAP()
462 if (handled)
463 return true;
465 return RenderWidgetFullscreen::OnMessageReceived(msg);
468 void RenderWidgetFullscreenPepper::WillInitiatePaint() {
469 if (plugin_)
470 plugin_->ViewWillInitiatePaint();
473 void RenderWidgetFullscreenPepper::DidInitiatePaint() {
474 if (plugin_)
475 plugin_->ViewInitiatedPaint();
478 void RenderWidgetFullscreenPepper::DidFlushPaint() {
479 if (plugin_)
480 plugin_->ViewFlushedPaint();
483 void RenderWidgetFullscreenPepper::Close() {
484 // If the fullscreen window is closed (e.g. user pressed escape), reset to
485 // normal mode.
486 if (plugin_)
487 plugin_->FlashSetFullscreen(false, false);
489 // Call Close on the base class to destroy the WebWidget instance.
490 RenderWidget::Close();
493 webkit::ppapi::PluginInstance*
494 RenderWidgetFullscreenPepper::GetBitmapForOptimizedPluginPaint(
495 const gfx::Rect& paint_bounds,
496 TransportDIB** dib,
497 gfx::Rect* location,
498 gfx::Rect* clip,
499 float* scale_factor) {
500 if (plugin_ && plugin_->GetBitmapForOptimizedPluginPaint(
501 paint_bounds, dib, location, clip, scale_factor)) {
502 return plugin_;
504 return NULL;
507 void RenderWidgetFullscreenPepper::OnResize(const gfx::Size& size,
508 const gfx::Rect& resizer_rect,
509 bool is_fullscreen) {
510 if (context_) {
511 gfx::Size pixel_size = gfx::ToFlooredSize(
512 gfx::ScaleSize(size, deviceScaleFactor()));
513 context_->reshape(pixel_size.width(), pixel_size.height());
514 context_->viewport(0, 0, pixel_size.width(), pixel_size.height());
516 RenderWidget::OnResize(size, resizer_rect, is_fullscreen);
519 WebWidget* RenderWidgetFullscreenPepper::CreateWebWidget() {
520 return new PepperWidget(this);
523 bool RenderWidgetFullscreenPepper::SupportsAsynchronousSwapBuffers() {
524 return context_ != NULL;
527 void RenderWidgetFullscreenPepper::CreateContext() {
528 DCHECK(!context_);
529 CommandLine* command_line = CommandLine::ForCurrentProcess();
530 if (command_line->HasSwitch(switches::kDisableFlashFullscreen3d))
531 return;
532 WebKit::WebGraphicsContext3D::Attributes attributes;
533 attributes.depth = false;
534 attributes.stencil = false;
535 attributes.antialias = false;
536 attributes.shareResources = false;
537 attributes.preferDiscreteGPU = true;
538 context_ = WebGraphicsContext3DCommandBufferImpl::CreateViewContext(
539 RenderThreadImpl::current(),
540 surface_id(),
541 NULL,
542 attributes,
543 true /* bind generates resources */,
544 active_url_,
545 CAUSE_FOR_GPU_LAUNCH_RENDERWIDGETFULLSCREENPEPPER_CREATECONTEXT);
546 if (!context_)
547 return;
549 if (!InitContext()) {
550 DestroyContext(context_, program_, buffer_);
551 context_ = NULL;
552 return;
556 namespace {
558 const char kVertexShader[] =
559 "attribute vec2 in_tex_coord;\n"
560 "varying vec2 tex_coord;\n"
561 "void main() {\n"
562 " gl_Position = vec4(in_tex_coord.x * 2. - 1.,\n"
563 " in_tex_coord.y * 2. - 1.,\n"
564 " 0.,\n"
565 " 1.);\n"
566 " tex_coord = vec2(in_tex_coord.x, in_tex_coord.y);\n"
567 "}\n";
569 const char kFragmentShader[] =
570 "precision mediump float;\n"
571 "varying vec2 tex_coord;\n"
572 "uniform sampler2D in_texture;\n"
573 "void main() {\n"
574 " gl_FragColor = texture2D(in_texture, tex_coord);\n"
575 "}\n";
577 GLuint CreateShaderFromSource(WebGraphicsContext3DCommandBufferImpl* context,
578 GLenum type,
579 const char* source) {
580 GLuint shader = context->createShader(type);
581 context->shaderSource(shader, source);
582 context->compileShader(shader);
583 int status = GL_FALSE;
584 context->getShaderiv(shader, GL_COMPILE_STATUS, &status);
585 if (!status) {
586 int size = 0;
587 context->getShaderiv(shader, GL_INFO_LOG_LENGTH, &size);
588 std::string log = context->getShaderInfoLog(shader).utf8();
589 DLOG(ERROR) << "Compilation failed: " << log;
590 context->deleteShader(shader);
591 shader = 0;
593 return shader;
596 const float kTexCoords[] = {
597 0.f, 0.f,
598 0.f, 2.f,
599 2.f, 0.f,
602 } // anonymous namespace
604 bool RenderWidgetFullscreenPepper::InitContext() {
605 gfx::Size pixel_size = gfx::ToFlooredSize(
606 gfx::ScaleSize(size(), deviceScaleFactor()));
607 context_->reshape(pixel_size.width(), pixel_size.height());
608 context_->viewport(0, 0, pixel_size.width(), pixel_size.height());
610 program_ = context_->createProgram();
612 GLuint vertex_shader =
613 CreateShaderFromSource(context_, GL_VERTEX_SHADER, kVertexShader);
614 if (!vertex_shader)
615 return false;
616 context_->attachShader(program_, vertex_shader);
617 context_->deleteShader(vertex_shader);
619 GLuint fragment_shader =
620 CreateShaderFromSource(context_, GL_FRAGMENT_SHADER, kFragmentShader);
621 if (!fragment_shader)
622 return false;
623 context_->attachShader(program_, fragment_shader);
624 context_->deleteShader(fragment_shader);
626 context_->bindAttribLocation(program_, 0, "in_tex_coord");
627 context_->linkProgram(program_);
628 int status = GL_FALSE;
629 context_->getProgramiv(program_, GL_LINK_STATUS, &status);
630 if (!status) {
631 int size = 0;
632 context_->getProgramiv(program_, GL_INFO_LOG_LENGTH, &size);
633 std::string log = context_->getProgramInfoLog(program_).utf8();
634 DLOG(ERROR) << "Link failed: " << log;
635 return false;
637 context_->useProgram(program_);
638 int texture_location = context_->getUniformLocation(program_, "in_texture");
639 context_->uniform1i(texture_location, 0);
641 buffer_ = context_->createBuffer();
642 context_->bindBuffer(GL_ARRAY_BUFFER, buffer_);
643 context_->bufferData(GL_ARRAY_BUFFER,
644 sizeof(kTexCoords),
645 kTexCoords,
646 GL_STATIC_DRAW);
647 context_->vertexAttribPointer(0, 2,
648 GL_FLOAT, GL_FALSE,
649 0, static_cast<WGC3Dintptr>(NULL));
650 context_->enableVertexAttribArray(0);
651 return true;
654 bool RenderWidgetFullscreenPepper::CheckCompositing() {
655 bool compositing =
656 webwidget_ && webwidget_->isAcceleratedCompositingActive();
657 if (compositing != is_accelerated_compositing_active_) {
658 if (compositing)
659 didActivateCompositor(-1);
660 else
661 didDeactivateCompositor();
663 return compositing;
666 void RenderWidgetFullscreenPepper::SwapBuffers() {
667 DCHECK(context_);
668 context_->prepareTexture();
670 // The compositor isn't actually active in this path, but pretend it is for
671 // scheduling purposes.
672 didCommitAndDrawCompositorFrame();
675 WebGraphicsContext3DCommandBufferImpl*
676 RenderWidgetFullscreenPepper::GetParentContextForPlatformContext3D() {
677 if (!context_) {
678 CreateContext();
680 if (!context_)
681 return NULL;
682 return context_;
685 } // namespace content