aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llmediactrl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linden/indra/newview/llmediactrl.cpp')
-rw-r--r--linden/indra/newview/llmediactrl.cpp1202
1 files changed, 1202 insertions, 0 deletions
diff --git a/linden/indra/newview/llmediactrl.cpp b/linden/indra/newview/llmediactrl.cpp
new file mode 100644
index 0000000..8b60326
--- /dev/null
+++ b/linden/indra/newview/llmediactrl.cpp
@@ -0,0 +1,1202 @@
1/**
2 * @file LLMediaCtrl.cpp
3 * @brief Web browser UI control
4 *
5 * $LicenseInfo:firstyear=2006&license=viewergpl$
6 *
7 * Copyright (c) 2006-2009, Linden Research, Inc.
8 *
9 * Second Life Viewer Source Code
10 * The source code in this file ("Source Code") is provided by Linden Lab
11 * to you under the terms of the GNU General Public License, version 2.0
12 * ("GPL"), unless you have obtained a separate licensing agreement
13 * ("Other License"), formally executed by you and Linden Lab. Terms of
14 * the GPL can be found in doc/GPL-license.txt in this distribution, or
15 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
16 *
17 * There are special exceptions to the terms and conditions of the GPL as
18 * it is applied to this Source Code. View the full text of the exception
19 * in the file doc/FLOSS-exception.txt in this software distribution, or
20 * online at
21 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
22 *
23 * By copying, modifying or distributing this software, you acknowledge
24 * that you have read and understood your obligations described above,
25 * and agree to abide by those obligations.
26 *
27 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
28 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
29 * COMPLETENESS OR PERFORMANCE.
30 * $/LicenseInfo$
31 */
32
33#include "llviewerprecompiledheaders.h"
34
35
36#include "llmediactrl.h"
37
38// viewer includes
39#include "llfloaterhtml.h"
40#include "llfloaterworldmap.h"
41#include "lluictrlfactory.h"
42#include "llurldispatcher.h"
43#include "llurlsimstring.h"
44#include "llviewborder.h"
45#include "llviewercontrol.h"
46#include "llviewermedia.h"
47#include "llviewerwindow.h"
48#include "llnotifications.h"
49#include "llweb.h"
50#include "llrender.h"
51#include "llpluginclassmedia.h"
52
53// linden library includes
54#include "llfocusmgr.h"
55
56extern BOOL gRestoreGL;
57
58// Setting the mozilla buffer width to 2048 exactly doesn't work, since it pads its rowbytes a bit, pushing the texture width over 2048.
59// 2000 should give enough headroom for any amount of padding it cares to add.
60const S32 MAX_DIMENSION = 2000;
61const S32 MAX_TEXTURE_DIMENSION = 2048;
62
63static LLRegisterWidget<LLMediaCtrl> r("web_browser");
64
65LLMediaCtrl::LLMediaCtrl( const std::string& name, const LLRect& rect ) :
66 LLUICtrl( name, rect, FALSE, NULL, NULL ),
67 mTextureDepthBytes( 4 ),
68 mWebBrowserImage( 0 ),
69 mBorder(NULL),
70 mFrequentUpdates( true ),
71 mForceUpdate( false ),
72 mOpenLinksInExternalBrowser( false ),
73 mOpenLinksInInternalBrowser( false ),
74 mTrusted( false ),
75 mHomePageUrl( "" ),
76 mIgnoreUIScale( true ),
77 mAlwaysRefresh( false ),
78 mExternalUrl( "" ),
79 mMediaSource( 0 ),
80 mTakeFocusOnClick( true ),
81 mCurrentNavUrl( "about:blank" ),
82 mLastSetCursor( UI_CURSOR_ARROW ),
83 mStretchToFill( true ),
84 mMaintainAspectRatio ( true ),
85 mHideLoading (false)
86{
87 S32 screen_width = mIgnoreUIScale ?
88 llround((F32)getRect().getWidth() * LLUI::sGLScaleFactor.mV[VX]) : getRect().getWidth();
89 S32 screen_height = mIgnoreUIScale ?
90 llround((F32)getRect().getHeight() * LLUI::sGLScaleFactor.mV[VY]) : getRect().getHeight();
91
92 mMediaSource = LLViewerMedia::newMediaImpl(mHomePageUrl, LLUUID::null, screen_width, screen_height, false, false, "text/html");
93 if ( !mMediaSource )
94 {
95 llwarns << "media source create failed " << llendl;
96 // return;
97 }
98 else
99 {
100 // create a new texture (based on LLDynamic texture) that will be used to display the output
101 mWebBrowserImage = new LLWebBrowserTexture( screen_width, screen_height, this, mMediaSource );
102 }
103
104 mMediaSource->setVisible( getVisible() );
105
106 mMediaSource->addObserver( this );
107
108 LLRect border_rect( 0, getRect().getHeight() + 2, getRect().getWidth() + 2, 0 );
109 mBorder = new LLViewBorder( std::string("web control border"), border_rect, LLViewBorder::BEVEL_IN );
110 addChild( mBorder );
111}
112
113////////////////////////////////////////////////////////////////////////////////
114// note: this is now a singleton and destruction happens via initClass() now
115LLMediaCtrl::~LLMediaCtrl()
116{
117
118 if (mMediaSource)
119 {
120 mMediaSource->remObserver( this );
121 mMediaSource = NULL;
122 }
123
124 if ( mWebBrowserImage )
125 {
126 delete mWebBrowserImage;
127 mWebBrowserImage = NULL;
128 }
129}
130
131////////////////////////////////////////////////////////////////////////////////
132//
133void LLMediaCtrl::setBorderVisible( BOOL border_visible )
134{
135 if ( mBorder )
136 {
137 mBorder->setVisible( border_visible );
138 };
139};
140
141////////////////////////////////////////////////////////////////////////////////
142//
143void LLMediaCtrl::setTakeFocusOnClick( bool take_focus )
144{
145 mTakeFocusOnClick = take_focus;
146}
147
148////////////////////////////////////////////////////////////////////////////////
149// set flag that forces the embedded browser to open links in the external system browser
150void LLMediaCtrl::setOpenInExternalBrowser( bool valIn )
151{
152 mOpenLinksInExternalBrowser = valIn;
153};
154
155////////////////////////////////////////////////////////////////////////////////
156// set flag that forces the embedded browser to open links in the internal browser floater
157void LLMediaCtrl::setOpenInInternalBrowser( bool valIn )
158{
159 mOpenLinksInInternalBrowser = valIn;
160};
161
162////////////////////////////////////////////////////////////////////////////////
163void LLMediaCtrl::setTrusted( bool valIn )
164{
165 mTrusted = valIn;
166}
167
168////////////////////////////////////////////////////////////////////////////////
169//
170BOOL LLMediaCtrl::handleHover( S32 x, S32 y, MASK mask )
171{
172 convertInputCoords(x, y);
173
174 if (mMediaSource)
175 mMediaSource->mouseMove(x, y);
176
177 gViewerWindow->setCursor(mLastSetCursor);
178
179 return TRUE;
180}
181
182////////////////////////////////////////////////////////////////////////////////
183//
184BOOL LLMediaCtrl::handleScrollWheel( S32 x, S32 y, S32 clicks )
185{
186 if (mMediaSource && mMediaSource->hasMedia())
187 mMediaSource->getMediaPlugin()->scrollEvent(0, clicks, MASK_NONE);
188
189 return TRUE;
190}
191
192////////////////////////////////////////////////////////////////////////////////
193//
194BOOL LLMediaCtrl::handleMouseUp( S32 x, S32 y, MASK mask )
195{
196 convertInputCoords(x, y);
197
198 if (mMediaSource)
199 {
200 mMediaSource->mouseUp(x, y);
201
202 // *HACK: media_plugin_webkit automatically takes focus on mouseup,
203 // in addition to the onFocusReceived() call below. Undo this. JC
204 // IMP-595: Is this really still the case for webkit?
205 if (!mTakeFocusOnClick)
206 {
207 mMediaSource->focus(false);
208 gViewerWindow->focusClient();
209 }
210 }
211
212 gFocusMgr.setMouseCapture( NULL );
213
214 return TRUE;
215}
216
217////////////////////////////////////////////////////////////////////////////////
218//
219BOOL LLMediaCtrl::handleMouseDown( S32 x, S32 y, MASK mask )
220{
221 convertInputCoords(x, y);
222
223 if (mMediaSource)
224 mMediaSource->mouseDown(x, y);
225
226 gFocusMgr.setMouseCapture( this );
227
228 if (mTakeFocusOnClick)
229 {
230 setFocus( TRUE );
231 }
232
233 return TRUE;
234}
235
236////////////////////////////////////////////////////////////////////////////////
237//
238BOOL LLMediaCtrl::handleDoubleClick( S32 x, S32 y, MASK mask )
239{
240 convertInputCoords(x, y);
241
242 if (mMediaSource)
243 mMediaSource->mouseLeftDoubleClick( x, y );
244
245 gFocusMgr.setMouseCapture( this );
246
247 if (mTakeFocusOnClick)
248 {
249 setFocus( TRUE );
250 }
251
252 return TRUE;
253}
254
255////////////////////////////////////////////////////////////////////////////////
256//
257void LLMediaCtrl::onFocusReceived()
258{
259 if (mMediaSource)
260 {
261 mMediaSource->focus(true);
262
263 // Set focus for edit menu items
264 LLEditMenuHandler::gEditMenuHandler = mMediaSource;
265 }
266
267 LLUICtrl::onFocusReceived();
268}
269
270////////////////////////////////////////////////////////////////////////////////
271//
272void LLMediaCtrl::onFocusLost()
273{
274 if (mMediaSource)
275 {
276 mMediaSource->focus(false);
277
278 if( LLEditMenuHandler::gEditMenuHandler == mMediaSource )
279 {
280 // Clear focus for edit menu items
281 LLEditMenuHandler::gEditMenuHandler = NULL;
282 }
283 }
284
285 gViewerWindow->focusClient();
286
287 LLUICtrl::onFocusLost();
288}
289
290////////////////////////////////////////////////////////////////////////////////
291//
292BOOL LLMediaCtrl::handleKeyHere( KEY key, MASK mask )
293{
294 BOOL result = FALSE;
295
296 // FIXME: THIS IS SO WRONG.
297 // Menu keys should be handled by the menu system and not passed to UI elements, but this is how LLTextEditor and LLLineEditor do it...
298
299 if (mMediaSource)
300 {
301 if( MASK_CONTROL & mask )
302 {
303 if( 'C' == key )
304 {
305 mMediaSource->copy();
306 result = TRUE;
307 }
308 else
309 if( 'V' == key )
310 {
311 mMediaSource->paste();
312 result = TRUE;
313 }
314 else
315 if( 'X' == key )
316 {
317 mMediaSource->cut();
318 result = TRUE;
319 }
320 }
321
322 if(!result)
323 {
324 result = mMediaSource->handleKeyHere(key, mask);
325 }
326 }
327
328 return result;
329}
330
331////////////////////////////////////////////////////////////////////////////////
332//
333void LLMediaCtrl::handleVisibilityChange ( BOOL new_visibility )
334{
335 llinfos << "visibility changed to " << (new_visibility?"true":"false") << llendl;
336 if(mMediaSource)
337 {
338 mMediaSource->setVisible( new_visibility );
339 }
340}
341
342////////////////////////////////////////////////////////////////////////////////
343//
344BOOL LLMediaCtrl::handleUnicodeCharHere(llwchar uni_char)
345{
346 BOOL result = FALSE;
347
348 // only accept 'printable' characters, sigh...
349 if (uni_char >= 32 // discard 'control' characters
350 && uni_char != 127) // SDL thinks this is 'delete' - yuck.
351 {
352 if (mMediaSource)
353 result = mMediaSource->handleUnicodeCharHere(uni_char);
354 }
355
356 return result;
357}
358
359////////////////////////////////////////////////////////////////////////////////
360//
361void LLMediaCtrl::onVisibilityChange ( BOOL new_visibility )
362{
363 // set state of frequent updates automatically if visibility changes
364 if ( new_visibility )
365 {
366 mFrequentUpdates = true;
367 }
368 else
369 {
370 mFrequentUpdates = false;
371 }
372 LLUICtrl::onVisibilityChange(new_visibility);
373}
374
375////////////////////////////////////////////////////////////////////////////////
376//
377void LLMediaCtrl::reshape( S32 width, S32 height, BOOL called_from_parent )
378{
379 S32 screen_width = mIgnoreUIScale ? llround((F32)width * LLUI::sGLScaleFactor.mV[VX]) : width;
380 S32 screen_height = mIgnoreUIScale ? llround((F32)height * LLUI::sGLScaleFactor.mV[VY]) : height;
381
382// llinfos << "reshape called with width = " << width << ", height = " << height << llendl;
383
384 // when floater is minimized, these sizes are negative
385 if ( mWebBrowserImage && screen_height > 0 && screen_width > 0 )
386 {
387 mWebBrowserImage->resize( screen_width, screen_height );
388 mForceUpdate = true;
389 }
390
391 LLUICtrl::reshape( width, height, called_from_parent );
392}
393
394////////////////////////////////////////////////////////////////////////////////
395//
396void LLMediaCtrl::navigateBack()
397{
398 if (mMediaSource && mMediaSource->hasMedia())
399 {
400 mMediaSource->getMediaPlugin()->browse_back();
401 }
402}
403
404////////////////////////////////////////////////////////////////////////////////
405//
406void LLMediaCtrl::navigateForward()
407{
408 if (mMediaSource && mMediaSource->hasMedia())
409 {
410 mMediaSource->getMediaPlugin()->browse_forward();
411 }
412}
413
414////////////////////////////////////////////////////////////////////////////////
415//
416bool LLMediaCtrl::canNavigateBack()
417{
418 if (mMediaSource)
419 return mMediaSource->canNavigateBack();
420 else
421 return false;
422}
423
424////////////////////////////////////////////////////////////////////////////////
425//
426bool LLMediaCtrl::canNavigateForward()
427{
428 if (mMediaSource)
429 return mMediaSource->canNavigateForward();
430 else
431 return false;
432}
433
434////////////////////////////////////////////////////////////////////////////////
435//
436void LLMediaCtrl::set404RedirectUrl( std::string redirect_url )
437{
438 if(mMediaSource && mMediaSource->hasMedia())
439 mMediaSource->getMediaPlugin()->set_status_redirect( 404, redirect_url );
440}
441
442////////////////////////////////////////////////////////////////////////////////
443//
444void LLMediaCtrl::clr404RedirectUrl()
445{
446 if(mMediaSource && mMediaSource->hasMedia())
447 mMediaSource->getMediaPlugin()->set_status_redirect(404, "");
448}
449
450////////////////////////////////////////////////////////////////////////////////
451//
452void LLMediaCtrl::navigateTo( std::string url_in, std::string mime_type)
453{
454 // don't browse to anything that starts with secondlife:// or sl://
455 const std::string protocol1 = "secondlife://";
456 const std::string protocol2 = "sl://";
457 if ((LLStringUtil::compareInsensitive(url_in.substr(0, protocol1.length()), protocol1) == 0) ||
458 (LLStringUtil::compareInsensitive(url_in.substr(0, protocol2.length()), protocol2) == 0))
459 {
460 // TODO: Print out/log this attempt?
461 // llinfos << "Rejecting attempt to load restricted website :" << urlIn << llendl;
462 return;
463 }
464
465 if (mMediaSource)
466 {
467 mCurrentNavUrl = url_in;
468 mMediaSource->navigateTo(url_in, mime_type, mime_type.empty());
469 }
470}
471
472////////////////////////////////////////////////////////////////////////////////
473//
474void LLMediaCtrl::navigateToLocalPage( const std::string& subdir, const std::string& filename_in )
475{
476 std::string language = LLUI::getLanguage();
477 std::string delim = gDirUtilp->getDirDelimiter();
478 std::string filename;
479
480 filename += subdir;
481 filename += delim;
482 filename += filename_in;
483
484 std::string expanded_filename = gDirUtilp->findSkinnedFilename("html", language, filename);
485
486 if (! gDirUtilp->fileExists(expanded_filename))
487 {
488 if (language != "en-us")
489 {
490 expanded_filename = gDirUtilp->findSkinnedFilename("html", "en-us", filename);
491 if (! gDirUtilp->fileExists(expanded_filename))
492 {
493 llwarns << "File " << subdir << delim << filename_in << "not found" << llendl;
494 return;
495 }
496 }
497 else
498 {
499 llwarns << "File " << subdir << delim << filename_in << "not found" << llendl;
500 return;
501 }
502 }
503 if (mMediaSource)
504 {
505 mCurrentNavUrl = expanded_filename;
506 mMediaSource->navigateTo(expanded_filename, "text/html", false);
507 }
508
509}
510
511////////////////////////////////////////////////////////////////////////////////
512//
513void LLMediaCtrl::navigateHome()
514{
515 if( mHomePageUrl.length() )
516 {
517 if (mMediaSource)
518 mMediaSource->navigateTo(mHomePageUrl);
519 };
520}
521
522////////////////////////////////////////////////////////////////////////////////
523//
524void LLMediaCtrl::setHomePageUrl( const std::string urlIn )
525{
526 mHomePageUrl = urlIn;
527}
528
529////////////////////////////////////////////////////////////////////////////////
530//
531bool LLMediaCtrl::setCaretColor(unsigned int red, unsigned int green, unsigned int blue)
532{
533 //NOOP
534 return false;
535}
536////////////////////////////////////////////////////////////////////////////////
537//
538std::string LLMediaCtrl::getHomePageUrl()
539{
540 return mHomePageUrl;
541}
542
543////////////////////////////////////////////////////////////////////////////////
544//
545LLPluginClassMedia* LLMediaCtrl::getMediaPlugin()
546{
547 return mMediaSource.isNull() ? NULL : mMediaSource->getMediaPlugin();
548}
549
550////////////////////////////////////////////////////////////////////////////////
551//
552void LLMediaCtrl::draw()
553{
554 if ( ! mWebBrowserImage )
555 return;
556
557 if ( gRestoreGL == 1 )
558 {
559 LLRect r = getRect();
560 reshape( r.getWidth(), r.getHeight(), FALSE );
561 return;
562 };
563
564 // NOTE: optimization needed here - probably only need to do this once
565 // unless tearoffs change the parent which they probably do.
566 const LLUICtrl* ptr = findRootMostFocusRoot();
567 if ( ptr && ptr->hasFocus() )
568 {
569 setFrequentUpdates( true );
570 }
571 else
572 {
573 setFrequentUpdates( false );
574 };
575
576 // alpha off for this
577 LLGLSUIDefault gls_ui;
578 LLGLDisable gls_alphaTest( GL_ALPHA_TEST );
579
580 gGL.pushMatrix();
581 {
582 if (mIgnoreUIScale)
583 {
584 glLoadIdentity();
585 // font system stores true screen origin, need to scale this by UI scale factor
586 // to get render origin for this view (with unit scale)
587 gGL.translatef(floorf(LLFontGL::sCurOrigin.mX * LLUI::sGLScaleFactor.mV[VX]),
588 floorf(LLFontGL::sCurOrigin.mY * LLUI::sGLScaleFactor.mV[VY]),
589 LLFontGL::sCurOrigin.mZ);
590 }
591
592 // scale texture to fit the space using texture coords
593 gGL.getTexUnit(0)->bind(mWebBrowserImage->getTexture());
594 gGL.color4fv( LLColor4::white.mV );
595 F32 max_u = ( F32 )mWebBrowserImage->getMediaWidth() / ( F32 )mWebBrowserImage->getWidth();
596 F32 max_v = ( F32 )mWebBrowserImage->getMediaHeight() / ( F32 )mWebBrowserImage->getHeight();
597
598 LLRect r = getRect();
599 S32 width, height;
600 S32 x_offset = 0;
601 S32 y_offset = 0;
602
603 if(mStretchToFill)
604 {
605 if(mMaintainAspectRatio)
606 {
607 F32 media_aspect = (F32)(mWebBrowserImage->getMediaWidth()) / (F32)(mWebBrowserImage->getMediaHeight());
608 F32 view_aspect = (F32)(r.getWidth()) / (F32)(r.getHeight());
609 if(media_aspect > view_aspect)
610 {
611 // max width, adjusted height
612 width = r.getWidth();
613 height = llmin(llmax(S32(width / media_aspect), 0), r.getHeight());
614 }
615 else
616 {
617 // max height, adjusted width
618 height = r.getHeight();
619 width = llmin(llmax(S32(height * media_aspect), 0), r.getWidth());
620 }
621 }
622 else
623 {
624 width = r.getWidth();
625 height = r.getHeight();
626 }
627 }
628 else
629 {
630 width = llmin(mWebBrowserImage->getMediaWidth(), r.getWidth());
631 height = llmin(mWebBrowserImage->getMediaHeight(), r.getHeight());
632 }
633
634 x_offset = (r.getWidth() - width) / 2;
635 y_offset = (r.getHeight() - height) / 2;
636
637 if (mIgnoreUIScale)
638 {
639 width = llround((F32)width * LLUI::sGLScaleFactor.mV[VX]);
640 height = llround((F32)height * LLUI::sGLScaleFactor.mV[VY]);
641 x_offset = llround((F32)x_offset * LLUI::sGLScaleFactor.mV[VX]);
642 y_offset = llround((F32)y_offset * LLUI::sGLScaleFactor.mV[VY]);
643 }
644
645 // draw the browser
646 gGL.setSceneBlendType(LLRender::BT_REPLACE);
647 gGL.begin( LLRender::QUADS );
648 if (! mWebBrowserImage->getTextureCoordsOpenGL())
649 {
650 // render using web browser reported width and height, instead of trying to invert GL scale
651 gGL.texCoord2f( max_u, 0.f );
652 gGL.vertex2i( x_offset + width, y_offset + height );
653
654 gGL.texCoord2f( 0.f, 0.f );
655 gGL.vertex2i( x_offset, y_offset + height );
656
657 gGL.texCoord2f( 0.f, max_v );
658 gGL.vertex2i( x_offset, y_offset );
659
660 gGL.texCoord2f( max_u, max_v );
661 gGL.vertex2i( x_offset + width, y_offset );
662 }
663 else
664 {
665 // render using web browser reported width and height, instead of trying to invert GL scale
666 gGL.texCoord2f( max_u, max_v );
667 gGL.vertex2i( x_offset + width, y_offset + height );
668
669 gGL.texCoord2f( 0.f, max_v );
670 gGL.vertex2i( x_offset, y_offset + height );
671
672 gGL.texCoord2f( 0.f, 0.f );
673 gGL.vertex2i( x_offset, y_offset );
674
675 gGL.texCoord2f( max_u, 0.f );
676 gGL.vertex2i( x_offset + width, y_offset );
677 }
678 gGL.end();
679 gGL.setSceneBlendType(LLRender::BT_ALPHA);
680 }
681 gGL.popMatrix();
682
683 // highlight if keyboard focus here. (TODO: this needs some work)
684 if ( mBorder->getVisible() )
685 mBorder->setKeyboardFocusHighlight( gFocusMgr.childHasKeyboardFocus( this ) );
686
687
688 LLUICtrl::draw();
689}
690
691////////////////////////////////////////////////////////////////////////////////
692//
693void LLMediaCtrl::convertInputCoords(S32& x, S32& y)
694{
695 x = mIgnoreUIScale ? llround((F32)x * LLUI::sGLScaleFactor.mV[VX]) : x;
696 if ( ! mWebBrowserImage->getTextureCoordsOpenGL() )
697 {
698 y = mIgnoreUIScale ? llround((F32)(y) * LLUI::sGLScaleFactor.mV[VY]) : y;
699 }
700 else
701 {
702 y = mIgnoreUIScale ? llround((F32)(getRect().getHeight() - y) * LLUI::sGLScaleFactor.mV[VY]) : getRect().getHeight() - y;
703 };
704}
705
706////////////////////////////////////////////////////////////////////////////////
707// static
708bool LLMediaCtrl::onClickLinkExternalTarget(const LLSD& notification, const LLSD& response )
709{
710 S32 option = LLNotification::getSelectedOption(notification, response);
711 if ( 0 == option )
712 {
713 // open in external browser because we don't support
714 // creation of our own secondary browser windows
715 LLWeb::loadURLExternal( notification["payload"]["external_url"].asString() );
716 }
717 return false;
718}
719
720////////////////////////////////////////////////////////////////////////////////
721// inherited from LLViewerMediaObserver
722//virtual
723void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event)
724{
725 switch(event)
726 {
727 case MEDIA_EVENT_CONTENT_UPDATED:
728 {
729 // LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CONTENT_UPDATED " << LL_ENDL;
730 };
731 break;
732
733 case MEDIA_EVENT_TIME_DURATION_UPDATED:
734 {
735 // LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_TIME_DURATION_UPDATED, time is " << self->getCurrentTime() << " of " << self->getDuration() << LL_ENDL;
736 };
737 break;
738
739 case MEDIA_EVENT_SIZE_CHANGED:
740 {
741 LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_SIZE_CHANGED " << LL_ENDL;
742 LLRect r = getRect();
743 reshape( r.getWidth(), r.getHeight(), FALSE );
744 };
745 break;
746
747 case MEDIA_EVENT_CURSOR_CHANGED:
748 {
749 LL_INFOS("Media") << "Media event: MEDIA_EVENT_CURSOR_CHANGED, new cursor is " << self->getCursorName() << LL_ENDL;
750
751 std::string cursor = self->getCursorName();
752
753 if(cursor == "arrow")
754 mLastSetCursor = UI_CURSOR_ARROW;
755 else if(cursor == "ibeam")
756 mLastSetCursor = UI_CURSOR_IBEAM;
757 else if(cursor == "splith")
758 mLastSetCursor = UI_CURSOR_SIZEWE;
759 else if(cursor == "splitv")
760 mLastSetCursor = UI_CURSOR_SIZENS;
761 else if(cursor == "hand")
762 mLastSetCursor = UI_CURSOR_HAND;
763 else // for anything else, default to the arrow
764 mLastSetCursor = UI_CURSOR_ARROW;
765 };
766 break;
767
768 case MEDIA_EVENT_NAVIGATE_BEGIN:
769 {
770 LL_INFOS("Media") << "Media event: MEDIA_EVENT_NAVIGATE_BEGIN, url is " << self->getNavigateURI() << LL_ENDL;
771 if(mMediaSource && mHideLoading)
772 {
773 mMediaSource->suspendUpdates(true);
774 }
775 };
776 break;
777
778 case MEDIA_EVENT_NAVIGATE_COMPLETE:
779 {
780 LL_INFOS("Media") << "Media event: MEDIA_EVENT_NAVIGATE_COMPLETE, result string is: " << self->getNavigateResultString() << LL_ENDL;
781 if(mMediaSource && mHideLoading)
782 {
783 mMediaSource->suspendUpdates(false);
784 }
785 };
786 break;
787
788 case MEDIA_EVENT_PROGRESS_UPDATED:
789 {
790 LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_PROGRESS_UPDATED, loading at " << self->getProgressPercent() << "%" << LL_ENDL;
791 };
792 break;
793
794 case MEDIA_EVENT_STATUS_TEXT_CHANGED:
795 {
796 LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_STATUS_TEXT_CHANGED, new status text is: " << self->getStatusText() << LL_ENDL;
797 };
798 break;
799
800 case MEDIA_EVENT_LOCATION_CHANGED:
801 {
802 LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_LOCATION_CHANGED, new uri is: " << self->getLocation() << LL_ENDL;
803 };
804 break;
805
806 case MEDIA_EVENT_CLICK_LINK_HREF:
807 {
808 LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLICK_LINK_HREF, target is \"" << self->getClickTarget() << "\", uri is " << self->getClickURL() << LL_ENDL;
809 onClickLinkHref(self);
810 };
811 break;
812
813 case MEDIA_EVENT_CLICK_LINK_NOFOLLOW:
814 {
815 LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLICK_LINK_NOFOLLOW, uri is " << self->getClickURL() << LL_ENDL;
816 onClickLinkNoFollow(self);
817 };
818 break;
819
820 case MEDIA_EVENT_PLUGIN_FAILED:
821 {
822 LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_PLUGIN_FAILED" << LL_ENDL;
823 };
824 break;
825
826 case MEDIA_EVENT_PLUGIN_FAILED_LAUNCH:
827 {
828 LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_PLUGIN_FAILED_LAUNCH" << LL_ENDL;
829 };
830 break;
831
832 case MEDIA_EVENT_NAME_CHANGED:
833 {
834 LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_NAME_CHANGED" << LL_ENDL;
835 };
836 break;
837 };
838
839 // chain all events to any potential observers of this object.
840 emitEvent(self, event);
841}
842
843////////////////////////////////////////////////////////////////////////////////
844//
845void LLMediaCtrl::onClickLinkHref( LLPluginClassMedia* self )
846{
847 // retrieve the event parameters
848 std::string target = self->getClickTarget();
849 std::string url = self->getClickURL();
850
851 // if there is a value for the target
852 if ( !target.empty() )
853 {
854 if ( target == "_external" )
855 {
856 mExternalUrl = url;
857 LLSD payload;
858 payload["external_url"] = mExternalUrl;
859 LLNotifications::instance().add( "WebLaunchExternalTarget", LLSD(), payload, onClickLinkExternalTarget);
860 return;
861 }
862 }
863
864 const std::string protocol1( "http://" );
865 const std::string protocol2( "https://" );
866 if( mOpenLinksInExternalBrowser )
867 {
868 if ( !url.empty() )
869 {
870 if ( LLStringUtil::compareInsensitive( url.substr( 0, protocol1.length() ), protocol1 ) == 0 ||
871 LLStringUtil::compareInsensitive( url.substr( 0, protocol2.length() ), protocol2 ) == 0 )
872 {
873 LLWeb::loadURLExternal( url );
874 }
875 }
876 }
877 else
878 if( mOpenLinksInInternalBrowser )
879 {
880 if ( !url.empty() )
881 {
882 if ( LLStringUtil::compareInsensitive( url.substr( 0, protocol1.length() ), protocol1 ) == 0 ||
883 LLStringUtil::compareInsensitive( url.substr( 0, protocol2.length() ), protocol2 ) == 0 )
884 {
885 // If we spawn a new LLFloaterHTML, assume we want it to
886 // follow this LLMediaCtrl's trust for whether or
887 // not to open secondlife:///app/ links. JC.
888// const bool open_links_externally = false;
889// LLFloaterHtml::getInstance()->show(
890// event_in.mStringPayload,
891// "Second Life Browser",
892// open_links_externally,
893// mTrusted);
894 }
895 }
896 }
897}
898
899////////////////////////////////////////////////////////////////////////////////
900//
901void LLMediaCtrl::onClickLinkNoFollow( LLPluginClassMedia* self )
902{
903 std::string url = self->getClickURL();
904 if (LLURLDispatcher::isSLURLCommand(url)
905 && !mTrusted)
906 {
907 // block handling of this secondlife:///app/ URL
908 LLNotifications::instance().add("UnableToOpenCommandURL");
909 return;
910 }
911
912 LLURLDispatcher::dispatch(url, this, mTrusted);
913}
914
915////////////////////////////////////////////////////////////////////////////////
916//
917LLWebBrowserTexture::LLWebBrowserTexture( S32 width, S32 height, LLMediaCtrl* browserCtrl, viewer_media_t media_source ) :
918 LLDynamicTexture( 512, 512, 4, ORDER_FIRST, TRUE ),
919 mNeedsUpdate( true ),
920 mNeedsResize( false ),
921 mTextureCoordsOpenGL( true ),
922 mWebBrowserCtrl( browserCtrl ),
923 mMediaSource(media_source)
924{
925 mElapsedTime.start();
926
927 resize( width, height );
928}
929
930////////////////////////////////////////////////////////////////////////////////
931//
932LLWebBrowserTexture::~LLWebBrowserTexture()
933{
934 mElapsedTime.stop();
935 mMediaSource = NULL;
936}
937
938////////////////////////////////////////////////////////////////////////////////
939//
940BOOL LLWebBrowserTexture::needsRender()
941{
942 bool texture_dirty = false;
943
944 if ( mWebBrowserCtrl->getFrequentUpdates() ||
945 mWebBrowserCtrl->getAlwaysRefresh() ||
946 mWebBrowserCtrl->getForceUpdate() )
947 {
948 // All of these force an update
949 return TRUE;
950 }
951
952 // If the texture needs updating, render needs to be called.
953 if (mMediaSource && mMediaSource->hasMedia())
954 {
955 LLPluginClassMedia* media = mMediaSource->getMediaPlugin();
956
957 if(media->textureValid() && media->getDirty())
958 {
959 texture_dirty = true;
960 }
961 }
962
963
964 return texture_dirty;
965}
966
967////////////////////////////////////////////////////////////////////////////////
968//
969BOOL LLWebBrowserTexture::render()
970{
971 if(updateBrowserTexture())
972 {
973 // updateBrowserTexture already verified that the media plugin is there and the texture is valid.
974 LLPluginClassMedia* media_plugin = mMediaSource->getMediaPlugin();
975 LLRect dirty_rect;
976
977 if(mNeedsUpdate)
978 {
979 // If we need an update, use the whole rect instead of the dirty rect.
980 dirty_rect.mLeft = 0;
981 dirty_rect.mBottom = 0;
982 dirty_rect.mRight = media_plugin->getWidth();
983 dirty_rect.mTop = media_plugin->getHeight();
984 }
985 else
986 {
987 mNeedsUpdate = media_plugin->getDirty(&dirty_rect);
988 }
989
990 if ( mNeedsUpdate )
991 {
992 mNeedsUpdate = false;
993 mWebBrowserCtrl->setForceUpdate(false);
994
995 // Constrain the dirty rect to be inside the texture
996 S32 x_pos = llmax(dirty_rect.mLeft, 0);
997 S32 y_pos = llmax(dirty_rect.mBottom, 0);
998 S32 width = llmin(dirty_rect.mRight, getWidth()) - x_pos;
999 S32 height = llmin(dirty_rect.mTop, getHeight()) - y_pos;
1000
1001 if(width > 0 && height > 0)
1002 {
1003 U8* data = media_plugin->getBitsData();
1004
1005 // Offset the pixels pointer to match x_pos and y_pos
1006 data += ( x_pos * media_plugin->getTextureDepth() * media_plugin->getBitsWidth() );
1007 data += ( y_pos * media_plugin->getTextureDepth() );
1008
1009 mTexture->setSubImage(
1010 data,
1011 media_plugin->getBitsWidth(),
1012 media_plugin->getBitsHeight(),
1013 x_pos,
1014 y_pos,
1015 width,
1016 height,
1017 TRUE); // force a fast update (i.e. don't call analyzeAlpha, etc.)
1018 }
1019
1020 media_plugin->resetDirty();
1021
1022 return TRUE;
1023 };
1024 };
1025
1026 return FALSE;
1027}
1028
1029////////////////////////////////////////////////////////////////////////////////
1030//
1031S32 LLWebBrowserTexture::getMediaWidth()
1032{
1033 return mMediaWidth;
1034}
1035
1036////////////////////////////////////////////////////////////////////////////////
1037//
1038S32 LLWebBrowserTexture::getMediaHeight()
1039{
1040 return mMediaHeight;
1041}
1042
1043////////////////////////////////////////////////////////////////////////////////
1044//
1045void LLWebBrowserTexture::setNeedsUpdate()
1046{
1047 mNeedsUpdate = true;
1048}
1049
1050////////////////////////////////////////////////////////////////////////////////
1051//
1052bool LLWebBrowserTexture::getNeedsUpdate()
1053{
1054 return mNeedsUpdate;
1055}
1056
1057////////////////////////////////////////////////////////////////////////////////
1058//
1059bool LLWebBrowserTexture::getTextureCoordsOpenGL()
1060{
1061 return mTextureCoordsOpenGL;
1062}
1063
1064
1065////////////////////////////////////////////////////////////////////////////////
1066//
1067void LLWebBrowserTexture::resize( S32 new_width, S32 new_height )
1068{
1069 F32 scale_ratio = 1.f;
1070 if (new_width > MAX_DIMENSION)
1071 {
1072 scale_ratio = (F32)MAX_DIMENSION / (F32)new_width;
1073 }
1074 if (new_height > MAX_DIMENSION)
1075 {
1076 scale_ratio = llmin(scale_ratio, (F32)MAX_DIMENSION / (F32)new_height);
1077 }
1078
1079 mMediaWidth = llround(scale_ratio * (F32)new_width);
1080 mMediaHeight = llround(scale_ratio * (F32)new_height);
1081
1082 adjustSize();
1083}
1084
1085bool LLWebBrowserTexture::adjustSize()
1086{
1087 if (mMediaSource && mMediaSource->hasMedia())
1088 {
1089 int natural_width = mMediaSource->getMediaPlugin()->getNaturalWidth();
1090 int natural_height = mMediaSource->getMediaPlugin()->getNaturalHeight();
1091
1092 if(natural_width != 0)
1093 {
1094 // If the media has a "natural size", use it.
1095 mMediaWidth = natural_width;
1096 mMediaHeight = natural_height;
1097 }
1098
1099 mMediaSource->setSize(mMediaWidth, mMediaHeight);
1100 mNeedsResize = false;
1101
1102 return true;
1103 }
1104 else
1105 {
1106 // The media isn't fully initialized yet, delay the resize until later.
1107 mNeedsResize = true;
1108 }
1109
1110 return false;
1111}
1112
1113bool LLWebBrowserTexture::updateBrowserTexture()
1114{
1115 if (!adjustSize())
1116 return false;
1117
1118 LLPluginClassMedia* media = mMediaSource->getMediaPlugin();
1119
1120 if(!media->textureValid())
1121 return false;
1122
1123 if(mMediaSource->mNeedsNewTexture
1124 || media->getTextureWidth() != mWidth
1125 || media->getTextureHeight() != mHeight )
1126 {
1127 releaseGLTexture();
1128
1129 mWidth = media->getTextureWidth();
1130 mHeight = media->getTextureHeight();
1131 mTextureCoordsOpenGL = media->getTextureCoordsOpenGL();
1132
1133 // will create mWidth * mHeight sized texture, using the texture params specified by the media.
1134 LLDynamicTexture::generateGLTexture(
1135 media->getTextureFormatInternal(),
1136 media->getTextureFormatPrimary(),
1137 media->getTextureFormatType(),
1138 media->getTextureFormatSwapBytes());
1139
1140
1141 mMediaSource->mNeedsNewTexture = false;
1142 }
1143
1144 return true;
1145}
1146// virtual
1147LLXMLNodePtr LLMediaCtrl::getXML(bool save_children) const
1148{
1149 LLXMLNodePtr node = LLUICtrl::getXML();
1150
1151 node->setName(LL_WEB_BROWSER_CTRL_TAG);
1152
1153 return node;
1154}
1155
1156LLView* LLMediaCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
1157{
1158 std::string name("web_browser");
1159 node->getAttributeString("name", name);
1160
1161 std::string start_url("");
1162 node->getAttributeString("start_url", start_url );
1163
1164 BOOL border_visible = true;
1165 node->getAttributeBOOL("border_visible", border_visible);
1166
1167 LLRect rect;
1168 createRect(node, rect, parent, LLRect());
1169
1170 LLMediaCtrl* web_browser = new LLMediaCtrl( name, rect );
1171
1172 if(node->hasAttribute("caret_color"))
1173 {
1174 LLColor4 color;
1175 LLUICtrlFactory::getAttributeColor(node, "caret_color", color);
1176 LLColor4U colorU = LLColor4U(color);
1177 web_browser->setCaretColor( colorU.mV[0], colorU.mV[1], colorU.mV[2] );
1178 }
1179
1180 BOOL ignore_ui_scale = web_browser->getIgnoreUIScale();
1181 node->getAttributeBOOL("ignore_ui_scale", ignore_ui_scale);
1182 web_browser->setIgnoreUIScale((bool)ignore_ui_scale);
1183
1184 web_browser->initFromXML(node, parent);
1185
1186 web_browser->setHomePageUrl( start_url );
1187
1188 web_browser->setBorderVisible( border_visible );
1189
1190 if(! start_url.empty())
1191 {
1192 web_browser->navigateHome();
1193 }
1194
1195 return web_browser;
1196}
1197
1198std::string LLMediaCtrl::getCurrentNavUrl()
1199{
1200 return mCurrentNavUrl;
1201}
1202