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.cpp1200
1 files changed, 1200 insertions, 0 deletions
diff --git a/linden/indra/newview/llmediactrl.cpp b/linden/indra/newview/llmediactrl.cpp
new file mode 100644
index 0000000..a517332
--- /dev/null
+++ b/linden/indra/newview/llmediactrl.cpp
@@ -0,0 +1,1200 @@
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: LLMediaImplLLMozLib automatically takes focus on mouseup,
203 // in addition to the onFocusReceived() call below. Undo this. JC
204 if (!mTakeFocusOnClick)
205 {
206 mMediaSource->focus(false);
207 gViewerWindow->focusClient();
208 }
209 }
210
211 gFocusMgr.setMouseCapture( NULL );
212
213 return TRUE;
214}
215
216////////////////////////////////////////////////////////////////////////////////
217//
218BOOL LLMediaCtrl::handleMouseDown( S32 x, S32 y, MASK mask )
219{
220 convertInputCoords(x, y);
221
222 if (mMediaSource)
223 mMediaSource->mouseDown(x, y);
224
225 gFocusMgr.setMouseCapture( this );
226
227 if (mTakeFocusOnClick)
228 {
229 setFocus( TRUE );
230 }
231
232 return TRUE;
233}
234
235////////////////////////////////////////////////////////////////////////////////
236//
237BOOL LLMediaCtrl::handleDoubleClick( S32 x, S32 y, MASK mask )
238{
239 convertInputCoords(x, y);
240
241 if (mMediaSource)
242 mMediaSource->mouseLeftDoubleClick( x, y );
243
244 gFocusMgr.setMouseCapture( this );
245
246 if (mTakeFocusOnClick)
247 {
248 setFocus( TRUE );
249 }
250
251 return TRUE;
252}
253
254////////////////////////////////////////////////////////////////////////////////
255//
256void LLMediaCtrl::onFocusReceived()
257{
258 if (mMediaSource)
259 {
260 mMediaSource->focus(true);
261
262 // Set focus for edit menu items
263 LLEditMenuHandler::gEditMenuHandler = mMediaSource;
264 }
265
266 LLUICtrl::onFocusReceived();
267}
268
269////////////////////////////////////////////////////////////////////////////////
270//
271void LLMediaCtrl::onFocusLost()
272{
273 if (mMediaSource)
274 {
275 mMediaSource->focus(false);
276
277 if( LLEditMenuHandler::gEditMenuHandler == mMediaSource )
278 {
279 // Clear focus for edit menu items
280 LLEditMenuHandler::gEditMenuHandler = NULL;
281 }
282 }
283
284 gViewerWindow->focusClient();
285
286 LLUICtrl::onFocusLost();
287}
288
289////////////////////////////////////////////////////////////////////////////////
290//
291BOOL LLMediaCtrl::handleKeyHere( KEY key, MASK mask )
292{
293 BOOL result = FALSE;
294
295 // FIXME: THIS IS SO WRONG.
296 // Menu keys should be handled by the menu system and not passed to UI elements, but this is how LLTextEditor and LLLineEditor do it...
297
298 if (mMediaSource)
299 {
300 if( MASK_CONTROL & mask )
301 {
302 if( 'C' == key )
303 {
304 mMediaSource->copy();
305 result = TRUE;
306 }
307 else
308 if( 'V' == key )
309 {
310 mMediaSource->paste();
311 result = TRUE;
312 }
313 else
314 if( 'X' == key )
315 {
316 mMediaSource->cut();
317 result = TRUE;
318 }
319 }
320
321 if(!result)
322 {
323 result = mMediaSource->handleKeyHere(key, mask);
324 }
325 }
326
327 return result;
328}
329
330////////////////////////////////////////////////////////////////////////////////
331//
332void LLMediaCtrl::handleVisibilityChange ( BOOL new_visibility )
333{
334 llinfos << "visibility changed to " << (new_visibility?"true":"false") << llendl;
335 if(mMediaSource)
336 {
337 mMediaSource->setVisible( new_visibility );
338 }
339}
340
341////////////////////////////////////////////////////////////////////////////////
342//
343BOOL LLMediaCtrl::handleUnicodeCharHere(llwchar uni_char)
344{
345 BOOL result = FALSE;
346
347 // only accept 'printable' characters, sigh...
348 if (uni_char >= 32 // discard 'control' characters
349 && uni_char != 127) // SDL thinks this is 'delete' - yuck.
350 {
351 if (mMediaSource)
352 result = mMediaSource->handleUnicodeCharHere(uni_char);
353 }
354
355 return result;
356}
357
358////////////////////////////////////////////////////////////////////////////////
359//
360void LLMediaCtrl::onVisibilityChange ( BOOL new_visibility )
361{
362 // set state of frequent updates automatically if visibility changes
363 if ( new_visibility )
364 {
365 mFrequentUpdates = true;
366 }
367 else
368 {
369 mFrequentUpdates = false;
370 }
371 LLUICtrl::onVisibilityChange(new_visibility);
372}
373
374////////////////////////////////////////////////////////////////////////////////
375//
376void LLMediaCtrl::reshape( S32 width, S32 height, BOOL called_from_parent )
377{
378 S32 screen_width = mIgnoreUIScale ? llround((F32)width * LLUI::sGLScaleFactor.mV[VX]) : width;
379 S32 screen_height = mIgnoreUIScale ? llround((F32)height * LLUI::sGLScaleFactor.mV[VY]) : height;
380
381// llinfos << "reshape called with width = " << width << ", height = " << height << llendl;
382
383 // when floater is minimized, these sizes are negative
384 if ( mWebBrowserImage && screen_height > 0 && screen_width > 0 )
385 {
386 mWebBrowserImage->resize( screen_width, screen_height );
387 mForceUpdate = true;
388 }
389
390 LLUICtrl::reshape( width, height, called_from_parent );
391}
392
393////////////////////////////////////////////////////////////////////////////////
394//
395void LLMediaCtrl::navigateBack()
396{
397 if (mMediaSource && mMediaSource->hasMedia())
398 {
399 mMediaSource->getMediaPlugin()->browse_back();
400 }
401}
402
403////////////////////////////////////////////////////////////////////////////////
404//
405void LLMediaCtrl::navigateForward()
406{
407 if (mMediaSource && mMediaSource->hasMedia())
408 {
409 mMediaSource->getMediaPlugin()->browse_forward();
410 }
411}
412
413////////////////////////////////////////////////////////////////////////////////
414//
415bool LLMediaCtrl::canNavigateBack()
416{
417 if (mMediaSource)
418 return mMediaSource->canNavigateBack();
419 else
420 return false;
421}
422
423////////////////////////////////////////////////////////////////////////////////
424//
425bool LLMediaCtrl::canNavigateForward()
426{
427 if (mMediaSource)
428 return mMediaSource->canNavigateForward();
429 else
430 return false;
431}
432
433////////////////////////////////////////////////////////////////////////////////
434//
435void LLMediaCtrl::set404RedirectUrl( std::string redirect_url )
436{
437 if(mMediaSource && mMediaSource->hasMedia())
438 mMediaSource->getMediaPlugin()->set_status_redirect( 404, redirect_url );
439}
440
441////////////////////////////////////////////////////////////////////////////////
442//
443void LLMediaCtrl::clr404RedirectUrl()
444{
445 if(mMediaSource && mMediaSource->hasMedia())
446 mMediaSource->getMediaPlugin()->set_status_redirect(404, "");
447}
448
449////////////////////////////////////////////////////////////////////////////////
450//
451void LLMediaCtrl::navigateTo( std::string url_in, std::string mime_type)
452{
453 // don't browse to anything that starts with secondlife:// or sl://
454 const std::string protocol1 = "secondlife://";
455 const std::string protocol2 = "sl://";
456 if ((LLStringUtil::compareInsensitive(url_in.substr(0, protocol1.length()), protocol1) == 0) ||
457 (LLStringUtil::compareInsensitive(url_in.substr(0, protocol2.length()), protocol2) == 0))
458 {
459 // TODO: Print out/log this attempt?
460 // llinfos << "Rejecting attempt to load restricted website :" << urlIn << llendl;
461 return;
462 }
463
464 if (mMediaSource)
465 {
466 mCurrentNavUrl = url_in;
467 mMediaSource->navigateTo(url_in, mime_type, mime_type.empty());
468 }
469}
470
471////////////////////////////////////////////////////////////////////////////////
472//
473void LLMediaCtrl::navigateToLocalPage( const std::string& subdir, const std::string& filename_in )
474{
475 std::string language = LLUI::getLanguage();
476 std::string delim = gDirUtilp->getDirDelimiter();
477 std::string filename;
478
479 filename += subdir;
480 filename += delim;
481 filename += filename_in;
482
483 std::string expanded_filename = gDirUtilp->findSkinnedFilename("html", language, filename);
484
485 if (! gDirUtilp->fileExists(expanded_filename))
486 {
487 if (language != "en-us")
488 {
489 expanded_filename = gDirUtilp->findSkinnedFilename("html", "en-us", filename);
490 if (! gDirUtilp->fileExists(expanded_filename))
491 {
492 llwarns << "File " << subdir << delim << filename_in << "not found" << llendl;
493 return;
494 }
495 }
496 else
497 {
498 llwarns << "File " << subdir << delim << filename_in << "not found" << llendl;
499 return;
500 }
501 }
502 if (mMediaSource)
503 {
504 mCurrentNavUrl = expanded_filename;
505 mMediaSource->navigateTo(expanded_filename, "text/html", false);
506 }
507
508}
509
510////////////////////////////////////////////////////////////////////////////////
511//
512void LLMediaCtrl::navigateHome()
513{
514 if( mHomePageUrl.length() )
515 {
516 if (mMediaSource)
517 mMediaSource->navigateTo(mHomePageUrl);
518 };
519}
520
521////////////////////////////////////////////////////////////////////////////////
522//
523void LLMediaCtrl::setHomePageUrl( const std::string urlIn )
524{
525 mHomePageUrl = urlIn;
526}
527
528////////////////////////////////////////////////////////////////////////////////
529//
530bool LLMediaCtrl::setCaretColor(unsigned int red, unsigned int green, unsigned int blue)
531{
532 //NOOP
533 return false;
534}
535////////////////////////////////////////////////////////////////////////////////
536//
537std::string LLMediaCtrl::getHomePageUrl()
538{
539 return mHomePageUrl;
540}
541
542////////////////////////////////////////////////////////////////////////////////
543//
544LLPluginClassMedia* LLMediaCtrl::getMediaPlugin()
545{
546 return mMediaSource.isNull() ? NULL : mMediaSource->getMediaPlugin();
547}
548
549////////////////////////////////////////////////////////////////////////////////
550//
551void LLMediaCtrl::draw()
552{
553 if ( ! mWebBrowserImage )
554 return;
555
556 if ( gRestoreGL == 1 )
557 {
558 LLRect r = getRect();
559 reshape( r.getWidth(), r.getHeight(), FALSE );
560 return;
561 };
562
563 // NOTE: optimization needed here - probably only need to do this once
564 // unless tearoffs change the parent which they probably do.
565 const LLUICtrl* ptr = findRootMostFocusRoot();
566 if ( ptr && ptr->hasFocus() )
567 {
568 setFrequentUpdates( true );
569 }
570 else
571 {
572 setFrequentUpdates( false );
573 };
574
575 // alpha off for this
576 LLGLSUIDefault gls_ui;
577 LLGLDisable gls_alphaTest( GL_ALPHA_TEST );
578
579 gGL.pushMatrix();
580 {
581 if (mIgnoreUIScale)
582 {
583 glLoadIdentity();
584 // font system stores true screen origin, need to scale this by UI scale factor
585 // to get render origin for this view (with unit scale)
586 gGL.translatef(floorf(LLFontGL::sCurOrigin.mX * LLUI::sGLScaleFactor.mV[VX]),
587 floorf(LLFontGL::sCurOrigin.mY * LLUI::sGLScaleFactor.mV[VY]),
588 LLFontGL::sCurOrigin.mZ);
589 }
590
591 // scale texture to fit the space using texture coords
592 gGL.getTexUnit(0)->bind(mWebBrowserImage->getTexture());
593 gGL.color4fv( LLColor4::white.mV );
594 F32 max_u = ( F32 )mWebBrowserImage->getMediaWidth() / ( F32 )mWebBrowserImage->getWidth();
595 F32 max_v = ( F32 )mWebBrowserImage->getMediaHeight() / ( F32 )mWebBrowserImage->getHeight();
596
597 LLRect r = getRect();
598 S32 width, height;
599 S32 x_offset = 0;
600 S32 y_offset = 0;
601
602 if(mStretchToFill)
603 {
604 if(mMaintainAspectRatio)
605 {
606 F32 media_aspect = (F32)(mWebBrowserImage->getMediaWidth()) / (F32)(mWebBrowserImage->getMediaHeight());
607 F32 view_aspect = (F32)(r.getWidth()) / (F32)(r.getHeight());
608 if(media_aspect > view_aspect)
609 {
610 // max width, adjusted height
611 width = r.getWidth();
612 height = llmin(llmax(S32(width / media_aspect), 0), r.getHeight());
613 }
614 else
615 {
616 // max height, adjusted width
617 height = r.getHeight();
618 width = llmin(llmax(S32(height * media_aspect), 0), r.getWidth());
619 }
620 }
621 else
622 {
623 width = r.getWidth();
624 height = r.getHeight();
625 }
626 }
627 else
628 {
629 width = llmin(mWebBrowserImage->getMediaWidth(), r.getWidth());
630 height = llmin(mWebBrowserImage->getMediaHeight(), r.getHeight());
631 }
632
633 x_offset = (r.getWidth() - width) / 2;
634 y_offset = (r.getHeight() - height) / 2;
635
636 if (mIgnoreUIScale)
637 {
638 width = llround((F32)width * LLUI::sGLScaleFactor.mV[VX]);
639 height = llround((F32)height * LLUI::sGLScaleFactor.mV[VY]);
640 x_offset = llround((F32)x_offset * LLUI::sGLScaleFactor.mV[VX]);
641 y_offset = llround((F32)y_offset * LLUI::sGLScaleFactor.mV[VY]);
642 }
643
644 // draw the browser
645 gGL.setSceneBlendType(LLRender::BT_REPLACE);
646 gGL.begin( LLRender::QUADS );
647 if (! mWebBrowserImage->getTextureCoordsOpenGL())
648 {
649 // render using web browser reported width and height, instead of trying to invert GL scale
650 gGL.texCoord2f( max_u, 0.f );
651 gGL.vertex2i( x_offset + width, y_offset + height );
652
653 gGL.texCoord2f( 0.f, 0.f );
654 gGL.vertex2i( x_offset, y_offset + height );
655
656 gGL.texCoord2f( 0.f, max_v );
657 gGL.vertex2i( x_offset, y_offset );
658
659 gGL.texCoord2f( max_u, max_v );
660 gGL.vertex2i( x_offset + width, y_offset );
661 }
662 else
663 {
664 // render using web browser reported width and height, instead of trying to invert GL scale
665 gGL.texCoord2f( max_u, max_v );
666 gGL.vertex2i( x_offset + width, y_offset + height );
667
668 gGL.texCoord2f( 0.f, max_v );
669 gGL.vertex2i( x_offset, y_offset + height );
670
671 gGL.texCoord2f( 0.f, 0.f );
672 gGL.vertex2i( x_offset, y_offset );
673
674 gGL.texCoord2f( max_u, 0.f );
675 gGL.vertex2i( x_offset + width, y_offset );
676 }
677 gGL.end();
678 gGL.setSceneBlendType(LLRender::BT_ALPHA);
679 }
680 gGL.popMatrix();
681
682 // highlight if keyboard focus here. (TODO: this needs some work)
683 if ( mBorder->getVisible() )
684 mBorder->setKeyboardFocusHighlight( gFocusMgr.childHasKeyboardFocus( this ) );
685
686
687 LLUICtrl::draw();
688}
689
690////////////////////////////////////////////////////////////////////////////////
691//
692void LLMediaCtrl::convertInputCoords(S32& x, S32& y)
693{
694 x = mIgnoreUIScale ? llround((F32)x * LLUI::sGLScaleFactor.mV[VX]) : x;
695 if ( ! mWebBrowserImage->getTextureCoordsOpenGL() )
696 {
697 y = mIgnoreUIScale ? llround((F32)(y) * LLUI::sGLScaleFactor.mV[VY]) : y;
698 }
699 else
700 {
701 y = mIgnoreUIScale ? llround((F32)(getRect().getHeight() - y) * LLUI::sGLScaleFactor.mV[VY]) : getRect().getHeight() - y;
702 };
703}
704
705////////////////////////////////////////////////////////////////////////////////
706// static
707bool LLMediaCtrl::onClickLinkExternalTarget(const LLSD& notification, const LLSD& response )
708{
709 S32 option = LLNotification::getSelectedOption(notification, response);
710 if ( 0 == option )
711 {
712 // open in external browser because we don't support
713 // creation of our own secondary browser windows
714 LLWeb::loadURLExternal( notification["payload"]["external_url"].asString() );
715 }
716 return false;
717}
718
719////////////////////////////////////////////////////////////////////////////////
720// inherited from LLViewerMediaObserver
721//virtual
722void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event)
723{
724 switch(event)
725 {
726 case MEDIA_EVENT_CONTENT_UPDATED:
727 {
728 // LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CONTENT_UPDATED " << LL_ENDL;
729 };
730 break;
731
732 case MEDIA_EVENT_TIME_DURATION_UPDATED:
733 {
734 // LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_TIME_DURATION_UPDATED, time is " << self->getCurrentTime() << " of " << self->getDuration() << LL_ENDL;
735 };
736 break;
737
738 case MEDIA_EVENT_SIZE_CHANGED:
739 {
740 LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_SIZE_CHANGED " << LL_ENDL;
741 LLRect r = getRect();
742 reshape( r.getWidth(), r.getHeight(), FALSE );
743 };
744 break;
745
746 case MEDIA_EVENT_CURSOR_CHANGED:
747 {
748 LL_INFOS("Media") << "Media event: MEDIA_EVENT_CURSOR_CHANGED, new cursor is " << self->getCursorName() << LL_ENDL;
749
750 std::string cursor = self->getCursorName();
751
752 if(cursor == "arrow")
753 mLastSetCursor = UI_CURSOR_ARROW;
754 else if(cursor == "ibeam")
755 mLastSetCursor = UI_CURSOR_IBEAM;
756 else if(cursor == "splith")
757 mLastSetCursor = UI_CURSOR_SIZEWE;
758 else if(cursor == "splitv")
759 mLastSetCursor = UI_CURSOR_SIZENS;
760 else if(cursor == "hand")
761 mLastSetCursor = UI_CURSOR_HAND;
762 else // for anything else, default to the arrow
763 mLastSetCursor = UI_CURSOR_ARROW;
764 };
765 break;
766
767 case MEDIA_EVENT_NAVIGATE_BEGIN:
768 {
769 LL_INFOS("Media") << "Media event: MEDIA_EVENT_NAVIGATE_BEGIN, url is " << self->getNavigateURI() << LL_ENDL;
770 if(mMediaSource && mHideLoading)
771 {
772 mMediaSource->suspendUpdates(true);
773 }
774 };
775 break;
776
777 case MEDIA_EVENT_NAVIGATE_COMPLETE:
778 {
779 LL_INFOS("Media") << "Media event: MEDIA_EVENT_NAVIGATE_COMPLETE, result string is: " << self->getNavigateResultString() << LL_ENDL;
780 if(mMediaSource && mHideLoading)
781 {
782 mMediaSource->suspendUpdates(false);
783 }
784 };
785 break;
786
787 case MEDIA_EVENT_PROGRESS_UPDATED:
788 {
789 LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_PROGRESS_UPDATED, loading at " << self->getProgressPercent() << "%" << LL_ENDL;
790 };
791 break;
792
793 case MEDIA_EVENT_STATUS_TEXT_CHANGED:
794 {
795 LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_STATUS_TEXT_CHANGED, new status text is: " << self->getStatusText() << LL_ENDL;
796 };
797 break;
798
799 case MEDIA_EVENT_LOCATION_CHANGED:
800 {
801 LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_LOCATION_CHANGED, new uri is: " << self->getLocation() << LL_ENDL;
802 };
803 break;
804
805 case MEDIA_EVENT_CLICK_LINK_HREF:
806 {
807 LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLICK_LINK_HREF, target is \"" << self->getClickTarget() << "\", uri is " << self->getClickURL() << LL_ENDL;
808 onClickLinkHref(self);
809 };
810 break;
811
812 case MEDIA_EVENT_CLICK_LINK_NOFOLLOW:
813 {
814 LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLICK_LINK_NOFOLLOW, uri is " << self->getClickURL() << LL_ENDL;
815 onClickLinkNoFollow(self);
816 };
817 break;
818
819 case MEDIA_EVENT_PLUGIN_FAILED:
820 {
821 LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_PLUGIN_FAILED" << LL_ENDL;
822 };
823 break;
824
825 case MEDIA_EVENT_PLUGIN_FAILED_LAUNCH:
826 {
827 LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_PLUGIN_FAILED_LAUNCH" << LL_ENDL;
828 };
829 break;
830
831 case MEDIA_EVENT_NAME_CHANGED:
832 {
833 LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_NAME_CHANGED" << LL_ENDL;
834 };
835 break;
836 };
837
838 // chain all events to any potential observers of this object.
839 emitEvent(self, event);
840}
841
842////////////////////////////////////////////////////////////////////////////////
843//
844void LLMediaCtrl::onClickLinkHref( LLPluginClassMedia* self )
845{
846 // retrieve the event parameters
847 std::string target = self->getClickTarget();
848 std::string url = self->getClickURL();
849
850 // if there is a value for the target
851 if ( !target.empty() )
852 {
853 if ( target == "_external" )
854 {
855 mExternalUrl = url;
856 LLSD payload;
857 payload["external_url"] = mExternalUrl;
858 LLNotifications::instance().add( "WebLaunchExternalTarget", LLSD(), payload, onClickLinkExternalTarget);
859 return;
860 }
861 }
862
863 const std::string protocol1( "http://" );
864 const std::string protocol2( "https://" );
865 if( mOpenLinksInExternalBrowser )
866 {
867 if ( !url.empty() )
868 {
869 if ( LLStringUtil::compareInsensitive( url.substr( 0, protocol1.length() ), protocol1 ) == 0 ||
870 LLStringUtil::compareInsensitive( url.substr( 0, protocol2.length() ), protocol2 ) == 0 )
871 {
872 LLWeb::loadURLExternal( url );
873 }
874 }
875 }
876 else
877 if( mOpenLinksInInternalBrowser )
878 {
879 if ( !url.empty() )
880 {
881 if ( LLStringUtil::compareInsensitive( url.substr( 0, protocol1.length() ), protocol1 ) == 0 ||
882 LLStringUtil::compareInsensitive( url.substr( 0, protocol2.length() ), protocol2 ) == 0 )
883 {
884 // If we spawn a new LLFloaterHTML, assume we want it to
885 // follow this LLMediaCtrl's trust for whether or
886 // not to open secondlife:///app/ links. JC.
887// const bool open_links_externally = false;
888// LLFloaterHtml::getInstance()->show(
889// event_in.mStringPayload,
890// "Second Life Browser",
891// open_links_externally,
892// mTrusted);
893 }
894 }
895 }
896}
897
898////////////////////////////////////////////////////////////////////////////////
899//
900void LLMediaCtrl::onClickLinkNoFollow( LLPluginClassMedia* self )
901{
902 std::string url = self->getClickURL();
903 if (LLURLDispatcher::isSLURLCommand(url)
904 && !mTrusted)
905 {
906 // block handling of this secondlife:///app/ URL
907 LLNotifications::instance().add("UnableToOpenCommandURL");
908 return;
909 }
910
911 LLURLDispatcher::dispatch(url, this, mTrusted);
912}
913
914////////////////////////////////////////////////////////////////////////////////
915//
916LLWebBrowserTexture::LLWebBrowserTexture( S32 width, S32 height, LLMediaCtrl* browserCtrl, viewer_media_t media_source ) :
917 LLDynamicTexture( 512, 512, 4, ORDER_FIRST, TRUE ),
918 mNeedsUpdate( true ),
919 mNeedsResize( false ),
920 mTextureCoordsOpenGL( true ),
921 mWebBrowserCtrl( browserCtrl ),
922 mMediaSource(media_source)
923{
924 mElapsedTime.start();
925
926 resize( width, height );
927}
928
929////////////////////////////////////////////////////////////////////////////////
930//
931LLWebBrowserTexture::~LLWebBrowserTexture()
932{
933 mElapsedTime.stop();
934 mMediaSource = NULL;
935}
936
937////////////////////////////////////////////////////////////////////////////////
938//
939BOOL LLWebBrowserTexture::needsRender()
940{
941 bool texture_dirty = false;
942
943 if ( mWebBrowserCtrl->getFrequentUpdates() ||
944 mWebBrowserCtrl->getAlwaysRefresh() ||
945 mWebBrowserCtrl->getForceUpdate() )
946 {
947 // All of these force an update
948 return TRUE;
949 }
950
951 // If the texture needs updating, render needs to be called.
952 if (mMediaSource && mMediaSource->hasMedia())
953 {
954 LLPluginClassMedia* media = mMediaSource->getMediaPlugin();
955
956 if(media->textureValid() && media->getDirty())
957 {
958 texture_dirty = true;
959 }
960 }
961
962
963 return texture_dirty;
964}
965
966////////////////////////////////////////////////////////////////////////////////
967//
968BOOL LLWebBrowserTexture::render()
969{
970 if(updateBrowserTexture())
971 {
972 // updateBrowserTexture already verified that the media plugin is there and the texture is valid.
973 LLPluginClassMedia* media_plugin = mMediaSource->getMediaPlugin();
974 LLRect dirty_rect;
975
976 if(mNeedsUpdate)
977 {
978 // If we need an update, use the whole rect instead of the dirty rect.
979 dirty_rect.mLeft = 0;
980 dirty_rect.mBottom = 0;
981 dirty_rect.mRight = media_plugin->getWidth();
982 dirty_rect.mTop = media_plugin->getHeight();
983 }
984 else
985 {
986 mNeedsUpdate = media_plugin->getDirty(&dirty_rect);
987 }
988
989 if ( mNeedsUpdate )
990 {
991 mNeedsUpdate = false;
992 mWebBrowserCtrl->setForceUpdate(false);
993
994 // Constrain the dirty rect to be inside the texture
995 S32 x_pos = llmax(dirty_rect.mLeft, 0);
996 S32 y_pos = llmax(dirty_rect.mBottom, 0);
997 S32 width = llmin(dirty_rect.mRight, getWidth()) - x_pos;
998 S32 height = llmin(dirty_rect.mTop, getHeight()) - y_pos;
999
1000 if(width > 0 && height > 0)
1001 {
1002 U8* data = media_plugin->getBitsData();
1003
1004 // Offset the pixels pointer to match x_pos and y_pos
1005 data += ( x_pos * media_plugin->getTextureDepth() * media_plugin->getBitsWidth() );
1006 data += ( y_pos * media_plugin->getTextureDepth() );
1007
1008 mTexture->setSubImage(
1009 data,
1010 media_plugin->getBitsWidth(),
1011 media_plugin->getBitsHeight(),
1012 x_pos,
1013 y_pos,
1014 width,
1015 height);
1016 }
1017
1018 media_plugin->resetDirty();
1019
1020 return TRUE;
1021 };
1022 };
1023
1024 return FALSE;
1025}
1026
1027////////////////////////////////////////////////////////////////////////////////
1028//
1029S32 LLWebBrowserTexture::getMediaWidth()
1030{
1031 return mMediaWidth;
1032}
1033
1034////////////////////////////////////////////////////////////////////////////////
1035//
1036S32 LLWebBrowserTexture::getMediaHeight()
1037{
1038 return mMediaHeight;
1039}
1040
1041////////////////////////////////////////////////////////////////////////////////
1042//
1043void LLWebBrowserTexture::setNeedsUpdate()
1044{
1045 mNeedsUpdate = true;
1046}
1047
1048////////////////////////////////////////////////////////////////////////////////
1049//
1050bool LLWebBrowserTexture::getNeedsUpdate()
1051{
1052 return mNeedsUpdate;
1053}
1054
1055////////////////////////////////////////////////////////////////////////////////
1056//
1057bool LLWebBrowserTexture::getTextureCoordsOpenGL()
1058{
1059 return mTextureCoordsOpenGL;
1060}
1061
1062
1063////////////////////////////////////////////////////////////////////////////////
1064//
1065void LLWebBrowserTexture::resize( S32 new_width, S32 new_height )
1066{
1067 F32 scale_ratio = 1.f;
1068 if (new_width > MAX_DIMENSION)
1069 {
1070 scale_ratio = (F32)MAX_DIMENSION / (F32)new_width;
1071 }
1072 if (new_height > MAX_DIMENSION)
1073 {
1074 scale_ratio = llmin(scale_ratio, (F32)MAX_DIMENSION / (F32)new_height);
1075 }
1076
1077 mMediaWidth = llround(scale_ratio * (F32)new_width);
1078 mMediaHeight = llround(scale_ratio * (F32)new_height);
1079
1080 adjustSize();
1081}
1082
1083bool LLWebBrowserTexture::adjustSize()
1084{
1085 if (mMediaSource && mMediaSource->hasMedia())
1086 {
1087 int natural_width = mMediaSource->getMediaPlugin()->getNaturalWidth();
1088 int natural_height = mMediaSource->getMediaPlugin()->getNaturalHeight();
1089
1090 if(natural_width != 0)
1091 {
1092 // If the media has a "natural size", use it.
1093 mMediaWidth = natural_width;
1094 mMediaHeight = natural_height;
1095 }
1096
1097 mMediaSource->setSize(mMediaWidth, mMediaHeight);
1098 mNeedsResize = false;
1099
1100 return true;
1101 }
1102 else
1103 {
1104 // The media isn't fully initialized yet, delay the resize until later.
1105 mNeedsResize = true;
1106 }
1107
1108 return false;
1109}
1110
1111bool LLWebBrowserTexture::updateBrowserTexture()
1112{
1113 if (!adjustSize())
1114 return false;
1115
1116 LLPluginClassMedia* media = mMediaSource->getMediaPlugin();
1117
1118 if(!media->textureValid())
1119 return false;
1120
1121 if(mMediaSource->mNeedsNewTexture
1122 || media->getTextureWidth() != mWidth
1123 || media->getTextureHeight() != mHeight )
1124 {
1125 releaseGLTexture();
1126
1127 mWidth = media->getTextureWidth();
1128 mHeight = media->getTextureHeight();
1129 mTextureCoordsOpenGL = media->getTextureCoordsOpenGL();
1130
1131 // will create mWidth * mHeight sized texture, using the texture params specified by the media.
1132 LLDynamicTexture::generateGLTexture(
1133 media->getTextureFormatInternal(),
1134 media->getTextureFormatPrimary(),
1135 media->getTextureFormatType(),
1136 media->getTextureFormatSwapBytes());
1137
1138
1139 mMediaSource->mNeedsNewTexture = false;
1140 }
1141
1142 return true;
1143}
1144// virtual
1145LLXMLNodePtr LLMediaCtrl::getXML(bool save_children) const
1146{
1147 LLXMLNodePtr node = LLUICtrl::getXML();
1148
1149 node->setName(LL_WEB_BROWSER_CTRL_TAG);
1150
1151 return node;
1152}
1153
1154LLView* LLMediaCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
1155{
1156 std::string name("web_browser");
1157 node->getAttributeString("name", name);
1158
1159 std::string start_url("");
1160 node->getAttributeString("start_url", start_url );
1161
1162 BOOL border_visible = true;
1163 node->getAttributeBOOL("border_visible", border_visible);
1164
1165 LLRect rect;
1166 createRect(node, rect, parent, LLRect());
1167
1168 LLMediaCtrl* web_browser = new LLMediaCtrl( name, rect );
1169
1170 if(node->hasAttribute("caret_color"))
1171 {
1172 LLColor4 color;
1173 LLUICtrlFactory::getAttributeColor(node, "caret_color", color);
1174 LLColor4U colorU = LLColor4U(color);
1175 web_browser->setCaretColor( colorU.mV[0], colorU.mV[1], colorU.mV[2] );
1176 }
1177
1178 BOOL ignore_ui_scale = web_browser->getIgnoreUIScale();
1179 node->getAttributeBOOL("ignore_ui_scale", ignore_ui_scale);
1180 web_browser->setIgnoreUIScale((bool)ignore_ui_scale);
1181
1182 web_browser->initFromXML(node, parent);
1183
1184 web_browser->setHomePageUrl( start_url );
1185
1186 web_browser->setBorderVisible( border_visible );
1187
1188 if(! start_url.empty())
1189 {
1190 web_browser->navigateHome();
1191 }
1192
1193 return web_browser;
1194}
1195
1196std::string LLMediaCtrl::getCurrentNavUrl()
1197{
1198 return mCurrentNavUrl;
1199}
1200