summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan "Geenz" Goodman <geenz@lindenlab.com>2026-10-08 17:31:29 -0400
committerGitHub <noreply@github.com>2026-10-08 17:31:29 -0400
commita4b3cdcee40bde061db9eb92977d7e9ffb185c77 (patch)
treec4ef66d6c70893ebb28d3fd9001f0dc8e1a9751a
parenteea02ed1683b34db7120b0cfa70bc4f9b063c9dc (diff)
parent4fd91009c30dd0479568013c94ab424c23721485 (diff)
Merge branch 'release/26.5' into project/chat-modernization
-rw-r--r--indra/llcommon/llfile.cpp20
-rw-r--r--indra/llui/llmodaldialog.cpp14
-rw-r--r--indra/llui/llmodaldialog.h3
-rw-r--r--indra/newview/CMakeLists.txt4
-rw-r--r--indra/newview/VIEWER_VERSION.txt2
-rw-r--r--indra/newview/app_settings/settings.xml55
-rw-r--r--indra/newview/llcommandhandler.cpp14
-rw-r--r--indra/newview/llcommandhandler.h4
-rw-r--r--indra/newview/llfloaterjoin.cpp150
-rw-r--r--indra/newview/llfloaterjoin.h51
-rw-r--r--indra/newview/llfloaterwebmodal.cpp63
-rw-r--r--indra/newview/llfloaterwebmodal.h48
-rw-r--r--indra/newview/llpanellogin.cpp24
-rw-r--r--indra/newview/llpanellogin.h1
-rw-r--r--indra/newview/llurldispatcher.cpp9
-rw-r--r--indra/newview/llviewerfloaterreg.cpp4
-rw-r--r--indra/newview/skins/default/xui/en/floater_join.xml60
-rw-r--r--indra/newview/skins/default/xui/en/floater_web_modal.xml23
18 files changed, 527 insertions, 22 deletions
diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp
index 114df29f6e..a33fcdc9c4 100644
--- a/indra/llcommon/llfile.cpp
+++ b/indra/llcommon/llfile.cpp
@@ -188,7 +188,7 @@ std::string strerr(int errn)
#if LL_WINDOWS && 0 // turn on to debug file-locking problems
#define PROCESS_LOCKING_CHECK 1
-static void find_locking_process(const std::string& filename)
+static void find_locking_process(const std::filesystem::path& filename)
{
// Only do any of this stuff (before LL_ENDL) if it will be logged.
LL_DEBUGS("LLFile") << "";
@@ -200,14 +200,14 @@ static void find_locking_process(const std::string& filename)
}
else
{
- std::string tf(TEMP);
- tf += "handle.tmp";
+ std::filesystem::path tf = fsyspath(TEMP) / "handle.tmp";
+ std::string utf8name = fsyspath(filename).string();
// http://technet.microsoft.com/en-us/sysinternals/bb896655
- std::string cmd(STRINGIZE("handle \"" << filename
- // "openfiles /query /v | fgrep -i \"" << filename
- << "\" > \"" << tf << '"'));
+ std::string cmd(STRINGIZE("handle \"" << utf8name
+ // "openfiles /query /v | fgrep -i \"" << utf8name
+ << "\" > \"" << fsyspath(tf).string() << '"'));
LL_CONT << cmd;
- if (system(cmd.c_str()) != 0)
+ if (_wsystem(ll_convert<std::wstring>(cmd).c_str()) != 0)
{
LL_CONT << "\nDownload 'handle.exe' from http://technet.microsoft.com/en-us/sysinternals/bb896655";
}
@@ -238,7 +238,7 @@ static int warnif(std::string_view desc, const std::filesystem::path& filename,
// EEXIST. Don't log a warning if caller explicitly says this errno is okay.
if (errn != suppress_warning)
{
- LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename << "' (errno " << errn << "): " << strerr(errn) << LL_ENDL;
+ LL_WARNS("LLFile") << "Couldn't " << desc << " '" << fsyspath(filename).string() << "' (errno " << errn << "): " << strerr(errn) << LL_ENDL;
}
#if PROCESS_LOCKING_CHECK
// If the problem is "Permission denied," maybe it's because another
@@ -267,7 +267,7 @@ static int warnif(std::string_view desc, const std::filesystem::path& filename,
// Don't warn if caller explicitly says this errno is okay.
if (errn != suppress_warning)
{
- LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename << "' (errno " << errn << "): " << ec.message() << LL_ENDL;
+ LL_WARNS("LLFile") << "Couldn't " << desc << " '" << fsyspath(filename).string() << "' (errno " << errn << "): " << ec.message() << LL_ENDL;
}
#if PROCESS_LOCKING_CHECK
// Try to detect locked files by other processes
@@ -988,7 +988,7 @@ bool LLFile::copy(const std::filesystem::path& source_path, const std::filesyste
bool copied = std::filesystem::copy_file(source_path, target_path, options, ec);
if (!copied)
{
- warnif(STRINGIZE("copy failed, to '" << target_path << "' from"), source_path, ec);
+ warnif(STRINGIZE("copy failed, to '" << fsyspath(target_path).string() << "' from"), source_path, ec);
}
return copied;
}
diff --git a/indra/llui/llmodaldialog.cpp b/indra/llui/llmodaldialog.cpp
index 225ff607ad..0db9c20b20 100644
--- a/indra/llui/llmodaldialog.cpp
+++ b/indra/llui/llmodaldialog.cpp
@@ -29,6 +29,7 @@
#include "llmodaldialog.h"
#include "llemojihelper.h"
+#include "llfloater.h"
#include "llfocusmgr.h"
#include "v4color.h"
#include "v2math.h"
@@ -40,9 +41,10 @@
// static
std::list<LLModalDialog*> LLModalDialog::sModalStack;
-LLModalDialog::LLModalDialog( const LLSD& key, bool modal )
+LLModalDialog::LLModalDialog( const LLSD& key, bool modal, bool hide_others )
: LLFloater(key),
- mModal( modal )
+ mModal( modal ),
+ mHideOthers( hide_others )
{
if (modal)
{
@@ -101,7 +103,7 @@ void LLModalDialog::onOpen(const LLSD& key)
if (mModal)
{
// If Modal, hide the active modal dialog
- if (!sModalStack.empty())
+ if (mHideOthers && !sModalStack.empty())
{
LLModalDialog* front = sModalStack.front();
if (front != this)
@@ -305,8 +307,10 @@ void LLModalDialog::draw()
void LLModalDialog::centerOnScreen()
{
- LLVector2 window_size = LLUI::getInstance()->getWindowSize();
- centerWithin(LLRect(0, 0, ll_round(window_size.mV[VX]), ll_round(window_size.mV[VY])));
+ // Use the floater view's rect (same as LLFloater::center()) rather than the raw
+ // window size, otherwise a reshape after the initial centering (e.g. triggered by embedded
+ // media content settling) re-centers the dialog against a different rect.
+ centerWithin(gFloaterView->getRect());
}
// static
diff --git a/indra/llui/llmodaldialog.h b/indra/llui/llmodaldialog.h
index 177664dde4..16ede3e515 100644
--- a/indra/llui/llmodaldialog.h
+++ b/indra/llui/llmodaldialog.h
@@ -39,7 +39,7 @@ class LLModalDialog;
class LLModalDialog : public LLFloater
{
public:
- LLModalDialog( const LLSD& key, bool modal = true );
+ LLModalDialog( const LLSD& key, bool modal = true, bool hide_others = true );
virtual ~LLModalDialog();
/*virtual*/ bool postBuild();
@@ -76,6 +76,7 @@ private:
LLFrameTimer mVisibleTime;
const bool mModal;
+ const bool mHideOthers;
static std::list<LLModalDialog*> sModalStack; // Top of stack is currently being displayed
};
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 6f69334c6e..c7a80a7a4b 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -254,6 +254,7 @@ set(viewer_SOURCE_FILES
llfloaterinspect.cpp
llfloaterinventorysettings.cpp
llfloaterinventorythumbnailshelper.cpp
+ llfloaterjoin.cpp
llfloaterjoystick.cpp
llfloaterlagmeter.cpp
llfloaterland.cpp
@@ -321,6 +322,7 @@ set(viewer_SOURCE_FILES
llfloaterurlentry.cpp
llfloatervoicevolume.cpp
llfloaterwebcontent.cpp
+ llfloaterwebmodal.cpp
llfloaterwhitelistentry.cpp
llfloaterwindowsize.cpp
llfloaterworldmap.cpp
@@ -944,6 +946,7 @@ set(viewer_HEADER_FILES
llfloaterinspect.h
llfloaterinventorysettings.h
llfloaterinventorythumbnailshelper.h
+ llfloaterjoin.h
llfloaterjoystick.h
llfloaterlagmeter.h
llfloaterland.h
@@ -1012,6 +1015,7 @@ set(viewer_HEADER_FILES
llfloaterurlentry.h
llfloatervoicevolume.h
llfloaterwebcontent.h
+ llfloaterwebmodal.h
llfloaterwhitelistentry.h
llfloaterwindowsize.h
llfloaterworldmap.h
diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt
index bb6eac98a9..3eb1386fcc 100644
--- a/indra/newview/VIEWER_VERSION.txt
+++ b/indra/newview/VIEWER_VERSION.txt
@@ -1 +1 @@
-26.4.0
+26.5.0
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 85285f1c6a..aa8d051439 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -3808,6 +3808,61 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>JoinFloaterOverlayOpacity</key>
+ <map>
+ <key>Comment</key>
+ <string>Opacity of the screen darkening overlay behind the Join floater</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.8</real>
+ </map>
+ <key>TermsAndConditionsURL</key>
+ <map>
+ <key>Comment</key>
+ <string>URL pointing to Terms and Conditions page</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>https://lindenlab.com/legal/second-life-terms-and-conditions</string>
+ </map>
+ <key>TermsOfServiceURL</key>
+ <map>
+ <key>Comment</key>
+ <string>URL pointing to Terms of Service page</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>https://lindenlab.com/tos</string>
+ </map>
+ <key>PrivacyPolicyURL</key>
+ <map>
+ <key>Comment</key>
+ <string>URL pointing to Privacy Policy page</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>https://lindenlab.com/privacy</string>
+ </map>
+ <key>JoinURL</key>
+ <map>
+ <key>Comment</key>
+ <string>URL opened in the Join floater on the login screen</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>https://join.secondlife.com/</string>
+ </map>
<key>JoystickAvatarEnabled</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/llcommandhandler.cpp b/indra/newview/llcommandhandler.cpp
index 4038ae8002..1ccf578118 100644
--- a/indra/newview/llcommandhandler.cpp
+++ b/indra/newview/llcommandhandler.cpp
@@ -66,6 +66,7 @@ public:
LLMediaCtrl* web,
const std::string& nav_type,
bool trusted_browser);
+ bool isSensitive(const std::string& cmd) const;
private:
void notifySlurlBlocked();
@@ -170,6 +171,13 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd,
return info.mHandler->handle(params, query_map, grid, web);
}
+bool LLCommandHandlerRegistry::isSensitive(const std::string& cmd) const
+{
+ std::map<std::string, LLCommandHandlerInfo>::const_iterator it = mMap.find(cmd);
+ if (it == mMap.end() || !it->second.mHandler) return false;
+ return it->second.mHandler->isSensitive();
+}
+
void LLCommandHandlerRegistry::notifySlurlBlocked()
{
static bool slurl_blocked = false;
@@ -231,6 +239,12 @@ bool LLCommandDispatcher::dispatch(const std::string& cmd,
cmd, params, query_map, grid, web, nav_type, trusted_browser);
}
+// static
+bool LLCommandDispatcher::isSensitive(const std::string& cmd)
+{
+ return LLCommandHandlerRegistry::instance().isSensitive(cmd);
+}
+
static std::string lookup(LLCommandHandler::EUntrustedAccess value);
LLSD LLCommandDispatcher::enumerate()
diff --git a/indra/newview/llcommandhandler.h b/indra/newview/llcommandhandler.h
index 7ec19d6538..f6c7565e6b 100644
--- a/indra/newview/llcommandhandler.h
+++ b/indra/newview/llcommandhandler.h
@@ -88,6 +88,8 @@ public:
const std::string& nav_type)
{ return true; }
+ virtual bool isSensitive() const { return false; }
+
virtual bool handle(const LLSD& params,
const LLSD& query_map,
const std::string& grid,
@@ -117,6 +119,8 @@ public:
/// Return an LLSD::Map of registered LLCommandHandlers and associated
/// info (e.g. EUntrustedAccess).
static LLSD enumerate();
+
+ static bool isSensitive(const std::string& cmd);
};
#endif
diff --git a/indra/newview/llfloaterjoin.cpp b/indra/newview/llfloaterjoin.cpp
new file mode 100644
index 0000000000..e82de2474f
--- /dev/null
+++ b/indra/newview/llfloaterjoin.cpp
@@ -0,0 +1,150 @@
+/**
+ * @file llfloaterjoin.cpp
+ * @brief Modal floater for the Join page on the login screen
+ *
+ * $LicenseInfo:firstyear=2026&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2026, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterjoin.h"
+
+#include "llcommandhandler.h"
+#include "llfloaterreg.h"
+#include "llframetimer.h"
+#include "llfloaterwebmodal.h"
+#include "llmediactrl.h"
+#include "llpanellogin.h"
+#include "llui.h"
+#include "llviewercontrol.h"
+
+// support for secondlife:///app/floaterjoin/{ACTION}/... SLapps
+class LLFloaterJoinHandler : public LLCommandHandler
+{
+public:
+ LLFloaterJoinHandler() : LLCommandHandler("floaterjoin", UNTRUSTED_THROTTLE) {}
+
+ // params/query_map can contain the join password, keep it out of the log
+ bool isSensitive() const override { return true; }
+
+ bool handle(const LLSD& params, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) override
+ {
+ if (params.size() < 1)
+ return false;
+
+ const std::string action = params[0].asString();
+
+ if (action == "close")
+ {
+ LLFloaterReg::hideInstance("join");
+ }
+ else if (action == "tos")
+ {
+ LLFloaterReg::showInstance("web_modal", LLSD().with("url", gSavedSettings.getString("TermsOfServiceURL")));
+ }
+ else if (action == "tc")
+ {
+ LLFloaterReg::showInstance("web_modal", LLSD().with("url", gSavedSettings.getString("TermsAndConditionsURL")));
+ }
+ else if (action == "privacy")
+ {
+ LLFloaterReg::showInstance("web_modal", LLSD().with("url", gSavedSettings.getString("PrivacyPolicyURL")));
+ }
+ else if (action == "join" && params.size() >= 3)
+ {
+ LLPanelLogin::setCredentialFields(LLURI::unescape(params[1].asString()),
+ LLURI::unescape(params[2].asString()));
+ LLFloaterReg::hideInstance("join");
+ }
+
+ return true;
+ }
+};
+LLFloaterJoinHandler gFloaterJoinHandler;
+
+LLFloaterJoin::LLFloaterJoin(const LLSD& key)
+ : LLModalDialog(key, /*modal=*/true)
+{
+}
+
+bool LLFloaterJoin::postBuild()
+{
+ mWebBrowser = getChild<LLMediaCtrl>("join_browser");
+ mNativeRect = getRect();
+ return true;
+}
+
+void LLFloaterJoin::updateFloaterSize()
+{
+ const LLRect& screen_rect = gFloaterView->getRect();
+ S32 width = llmin(mNativeRect.getWidth(), screen_rect.getWidth());
+ S32 height = llmin(mNativeRect.getHeight(), screen_rect.getHeight());
+
+ // Shrink the floater to fit the screen, letting scroll container reveal the rest if
+ // the screen is currently smaller than the floater's native size, else restore it
+ if (width != getRect().getWidth() || height != getRect().getHeight())
+ {
+ reshape(width, height);
+ center();
+ }
+}
+
+void LLFloaterJoin::draw()
+{
+ static LLCachedControl<F32> overlay_opacity(gSavedSettings, "JoinFloaterOverlayOpacity");
+
+ updateFloaterSize();
+
+ // As a modal dialog this floater is drawn twice per frame (once by LLFloaterView and once
+ // by LLPopupView), so paint the overlay only on the first pass of each frame
+ U32 frame_count = LLFrameTimer::getFrameCount();
+ if (mOverlayFrame != frame_count)
+ {
+ mOverlayFrame = frame_count;
+
+ // darken everything behind the floater, in absolute window coordinates
+ LLVector2 window_size = LLUI::getInstance()->getWindowSize();
+ LLUI::pushMatrix();
+ LLUI::loadIdentity();
+ gl_rect_2d(0, ll_round(window_size.mV[VY]), ll_round(window_size.mV[VX]), 0,
+ LLColor4(0.f, 0.f, 0.f, llclamp((F32)overlay_opacity, 0.f, 1.f)));
+ LLUI::popMatrix();
+ }
+
+ LLModalDialog::draw();
+}
+
+void LLFloaterJoin::onOpen(const LLSD& key)
+{
+ LLModalDialog::onOpen(key);
+
+ std::string url = gSavedSettings.getString("JoinURL");
+ if (key.has("url"))
+ {
+ url = key["url"].asString();
+ }
+
+ if (mWebBrowser && !url.empty())
+ {
+ mWebBrowser->navigateTo(url, HTTP_CONTENT_TEXT_HTML);
+ }
+}
diff --git a/indra/newview/llfloaterjoin.h b/indra/newview/llfloaterjoin.h
new file mode 100644
index 0000000000..5922a45e85
--- /dev/null
+++ b/indra/newview/llfloaterjoin.h
@@ -0,0 +1,51 @@
+/**
+ * @file llfloaterjoin.h
+ * @brief Modal floater for the Join page on the login screen
+ *
+ * $LicenseInfo:firstyear=2026&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2026, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#pragma once
+
+#include "llmodaldialog.h"
+
+class LLMediaCtrl;
+
+class LLFloaterJoin : public LLModalDialog
+{
+ friend class LLFloaterReg;
+public:
+ bool postBuild() override;
+ void onOpen(const LLSD& key) override;
+ void draw() override;
+
+private:
+ LLFloaterJoin(const LLSD& key);
+ ~LLFloaterJoin() = default;
+
+ void updateFloaterSize();
+
+ LLMediaCtrl* mWebBrowser = nullptr;
+ U32 mOverlayFrame = 0;
+ LLRect mNativeRect;
+};
+
diff --git a/indra/newview/llfloaterwebmodal.cpp b/indra/newview/llfloaterwebmodal.cpp
new file mode 100644
index 0000000000..ac74af5d6f
--- /dev/null
+++ b/indra/newview/llfloaterwebmodal.cpp
@@ -0,0 +1,63 @@
+/**
+ * @file llfloaterwebmodal.cpp
+ * @brief Modal floater for displaying a web page above other modal floaters
+ *
+ * $LicenseInfo:firstyear=2026&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2026, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterwebmodal.h"
+
+#include "llmediactrl.h"
+
+LLFloaterWebModal::LLFloaterWebModal(const LLSD& key)
+ // don't hide an existing modal floater (e.g. LLFloaterJoin) behind this one
+ : LLModalDialog(key, /*modal=*/true, /*hide_others=*/false)
+{
+}
+
+bool LLFloaterWebModal::postBuild()
+{
+ mWebBrowser = getChild<LLMediaCtrl>("web_modal_browser");
+ mWebBrowser->addObserver(this);
+ return true;
+}
+
+void LLFloaterWebModal::onOpen(const LLSD& key)
+{
+ LLModalDialog::onOpen(key);
+
+ if (mWebBrowser && key.has("url"))
+ {
+ mWebBrowser->navigateTo(key["url"].asString(), HTTP_CONTENT_TEXT_HTML);
+ }
+}
+
+void LLFloaterWebModal::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event)
+{
+ if (event == MEDIA_EVENT_NAME_CHANGED)
+ {
+ const std::string page_title = self->getMediaName();
+ setTitle(page_title.empty() ? self->getLocation() : page_title);
+ }
+}
diff --git a/indra/newview/llfloaterwebmodal.h b/indra/newview/llfloaterwebmodal.h
new file mode 100644
index 0000000000..fc10be3535
--- /dev/null
+++ b/indra/newview/llfloaterwebmodal.h
@@ -0,0 +1,48 @@
+/**
+ * @file llfloaterwebmodal.h
+ * @brief Modal floater for displaying a web page above other modal floaters
+ *
+ * $LicenseInfo:firstyear=2026&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2026, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#pragma once
+
+#include "llmodaldialog.h"
+#include "llviewermediaobserver.h"
+
+class LLMediaCtrl;
+
+class LLFloaterWebModal : public LLModalDialog, public LLViewerMediaObserver
+{
+ friend class LLFloaterReg;
+public:
+ bool postBuild() override;
+ void onOpen(const LLSD& key) override;
+
+ void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) override;
+
+private:
+ LLFloaterWebModal(const LLSD& key);
+ ~LLFloaterWebModal() = default;
+
+ LLMediaCtrl* mWebBrowser = nullptr;
+};
diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp
index 9b5a339958..77b09a610c 100644
--- a/indra/newview/llpanellogin.cpp
+++ b/indra/newview/llpanellogin.cpp
@@ -65,6 +65,7 @@
#include "llmediactrl.h"
#include "llrootview.h"
+#include "llfloaterjoin.h"
#include "llfloatertos.h"
#include "lltrans.h"
#include "llglheaders.h"
@@ -536,6 +537,24 @@ void LLPanelLogin::reshapePanel()
}
//static
+void LLPanelLogin::setCredentialFields(const std::string& username, const std::string& password)
+{
+ if (!sInstance)
+ return;
+ sInstance->getChild<LLComboBox>("username_combo")->setLabel(username);
+ sInstance->mUsernameLength = static_cast<unsigned int>(username.length());
+ sInstance->getChild<LLUICtrl>("password_edit")->setValue(password);
+ sInstance->mPasswordLength = static_cast<unsigned int>(password.length());
+ sInstance->mPasswordModified = true;
+ sInstance->updateLoginButtons();
+
+ sInstance->getChild<LLUICtrl>("remember_name")->setValue(true);
+ LLUICtrl* remember_password = sInstance->getChild<LLUICtrl>("remember_password");
+ remember_password->setValue(true);
+ remember_password->setEnabled(true);
+}
+
+//static
void LLPanelLogin::populateFields(LLPointer<LLCredential> credential, bool remember_user, bool remember_psswrd)
{
if (!sInstance)
@@ -1057,10 +1076,7 @@ void LLPanelLogin::onClickForgotPassword(void*)
//static
void LLPanelLogin::onClickSignUp(void*)
{
- if (sInstance)
- {
- LLWeb::loadURLExternal(sInstance->getString("sign_up_url"));
- }
+ LLFloaterReg::showInstance("join");
}
// static
diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h
index a00081795e..453b645299 100644
--- a/indra/newview/llpanellogin.h
+++ b/indra/newview/llpanellogin.h
@@ -60,6 +60,7 @@ public:
static void populateFields(LLPointer<LLCredential> credential, bool remember_user, bool remember_psswrd);
static void resetFields();
static void getFields(LLPointer<LLCredential>& credential, bool& remember_user, bool& remember_psswrd);
+ static void setCredentialFields(const std::string& username, const std::string& password);
static bool isCredentialSet() { return sCredentialSet; }
diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp
index 39a9f0f8bc..8caecbae77 100644
--- a/indra/newview/llurldispatcher.cpp
+++ b/indra/newview/llurldispatcher.cpp
@@ -159,7 +159,14 @@ bool LLURLDispatcherImpl::dispatchApp(const LLSLURL& slurl,
LLMediaCtrl* web,
bool trusted_browser)
{
- LL_INFOS() << "cmd: " << slurl.getAppCmd() << " path: " << slurl.getAppPath() << " query: " << slurl.getAppQuery() << LL_ENDL;
+ // don't log path/query for sensitive commands
+ std::string app_info = "cmd: " + slurl.getAppCmd();
+ if (!LLCommandDispatcher::isSensitive(slurl.getAppCmd()))
+ {
+ app_info += " path: " + slurl.getAppPath().asString() + " query: " + slurl.getAppQuery();
+ }
+ LL_INFOS() << app_info << LL_ENDL;
+
const LLSD& query_map = LLURI::queryMap(slurl.getAppQuery());
bool handled = LLCommandDispatcher::dispatch(
slurl.getAppCmd(), slurl.getAppPath(), query_map, slurl.getGrid(), web, nav_type, trusted_browser);
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index 0cfbcc06c0..7d3b554709 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -92,6 +92,7 @@
#include "llfloaterinspect.h"
#include "llfloaterinventorysettings.h"
#include "llfloaterinventorythumbnailshelper.h"
+#include "llfloaterjoin.h"
#include "llfloaterjoystick.h"
#include "llfloaterlagmeter.h"
#include "llfloaterland.h"
@@ -154,6 +155,7 @@
#include "llfloatertranslationsettings.h"
#include "llfloateruipreview.h"
#include "llfloaterwebcontent.h"
+#include "llfloaterwebmodal.h"
#include "llfloatervoicevolume.h"
#include "llfloaterwhitelistentry.h"
#include "llfloaterwindowsize.h"
@@ -406,6 +408,7 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("item_properties", "floater_item_properties.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterItemProperties>);
LLFloaterReg::add("task_properties", "floater_task_properties.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterItemProperties>);
LLFloaterReg::add("inventory_settings", "floater_inventory_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterInventorySettings>);
+ LLFloaterReg::add("join", "floater_join.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterJoin>);
LLInspectAvatarUtil::registerFloater();
LLInspectGroupUtil::registerFloater();
LLInspectObjectUtil::registerFloater();
@@ -519,6 +522,7 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("upload_sound", "floater_sound_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSoundPreview>, "upload");
LLFloaterReg::add("web_content", "floater_web_content.xml", (LLFloaterBuildFunc)&LLFloaterWebContent::create);
+ LLFloaterReg::add("web_modal", "floater_web_modal.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterWebModal>);
LLFloaterReg::add("whitelist_entry", "floater_whitelist_entry.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterWhiteListEntry>);
LLFloaterReg::add("window_size", "floater_window_size.xml", &LLFloaterReg::build<LLFloaterWindowSize>);
LLFloaterReg::add("world_map", "floater_world_map.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterWorldMap>);
diff --git a/indra/newview/skins/default/xui/en/floater_join.xml b/indra/newview/skins/default/xui/en/floater_join.xml
new file mode 100644
index 0000000000..fac0293097
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_join.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater
+ height="768"
+ width="1024"
+ layout="topleft"
+ name="floater_join"
+ single_instance="true"
+ show_title="false"
+ legacy_header_height="0"
+ header_height="0"
+ background_visible="false"
+ can_resize="false"
+ can_drag_on_left="false"
+ can_minimize="false"
+ can_close="false"
+ positioning="centered"
+ save_rect="false">
+ <!-- web_browser is wrapped in a scroll container so it stays reachable via scrollbars if the viewer window is too small-->
+ <scroll_container
+ follows="all"
+ left="0"
+ top="0"
+ right="-1"
+ bottom="-1"
+ name="join_scroll"
+ opaque="false"
+ border_visible="false"
+ mouse_opaque="false">
+ <panel
+ follows="left|top"
+ left="0"
+ top="0"
+ width="1024"
+ height="768"
+ name="web_panel"
+ background_visible="false"
+ mouse_opaque="false">
+ <web_browser
+ follows="all"
+ left="0"
+ top="0"
+ right="-1"
+ bottom="-1"
+ name="join_browser"
+ trusted_content="false"
+ border_visible="false" />
+ </panel>
+ </scroll_container>
+ <view_border
+ follows="all"
+ left="0"
+ top="0"
+ right="-1"
+ bottom="-1"
+ border_thickness="1"
+ bevel_style="none"
+ highlight_light_color="White"
+ mouse_opaque="false"
+ name="floater_outline" />
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_web_modal.xml b/indra/newview/skins/default/xui/en/floater_web_modal.xml
new file mode 100644
index 0000000000..428522a403
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_web_modal.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater
+ legacy_header_height="18"
+ height="600"
+ width="800"
+ layout="topleft"
+ name="floater_web_modal"
+ title=""
+ can_resize="true"
+ can_minimize="false"
+ can_close="true"
+ positioning="centered"
+ save_rect="false">
+ <web_browser
+ follows="all"
+ left="0"
+ top="20"
+ right="-1"
+ bottom="-1"
+ name="web_modal_browser"
+ trusted_content="false"
+ border_visible="false" />
+</floater>