Merge remote-tracking branch 'hifi/master' into android_friends_main

This commit is contained in:
Cristian Luis Duarte 2018-08-06 16:07:44 -03:00
commit efdafc2a8f
54 changed files with 688 additions and 389 deletions

View file

@ -827,10 +827,6 @@ void Agent::processAgentAvatarAudio() {
void Agent::aboutToFinish() {
setIsAvatar(false);// will stop timers for sending identity packets
if (_scriptEngine) {
_scriptEngine->stop();
}
// our entity tree is going to go away so tell that to the EntityScriptingInterface
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(nullptr);
@ -843,7 +839,6 @@ void Agent::aboutToFinish() {
// destroy all other created dependencies
DependencyManager::destroy<ScriptCache>();
DependencyManager::destroy<ScriptEngines>();
DependencyManager::destroy<ResourceCacheSharedItems>();
DependencyManager::destroy<SoundCacheScriptingInterface>();
@ -862,3 +857,11 @@ void Agent::aboutToFinish() {
_encoder = nullptr;
}
}
void Agent::stop() {
if (_scriptEngine) {
_scriptEngine->stop();
} else {
setFinished(true);
}
}

View file

@ -67,6 +67,8 @@ public slots:
void setIsAvatar(bool isAvatar);
bool isAvatar() const { return _isAvatar; }
Q_INVOKABLE virtual void stop() override;
private slots:
void requestScript();
void scriptRequestFinished();

View file

@ -46,6 +46,14 @@
"default": "40102",
"type": "int",
"advanced": true
},
{
"name": "enable_packet_verification",
"label": "Enable Packet Verification",
"help": "Enable secure checksums on communication that uses the High Fidelity protocol. Increases security with possibly a small performance penalty.",
"default": true,
"type": "checkbox",
"advanced": true
}
]
},

View file

@ -630,6 +630,7 @@ bool DomainServer::isPacketVerified(const udt::Packet& packet) {
void DomainServer::setupNodeListAndAssignments() {
const QString CUSTOM_LOCAL_PORT_OPTION = "metaverse.local_port";
static const QString ENABLE_PACKET_AUTHENTICATION = "metaverse.enable_packet_verification";
QVariant localPortValue = _settingsManager.valueOrDefaultValueForKeyPath(CUSTOM_LOCAL_PORT_OPTION);
int domainServerPort = localPortValue.toInt();
@ -696,6 +697,9 @@ void DomainServer::setupNodeListAndAssignments() {
}
}
bool isAuthEnabled = _settingsManager.valueOrDefaultValueForKeyPath(ENABLE_PACKET_AUTHENTICATION).toBool();
nodeList->setAuthenticatePackets(isAuthEnabled);
connect(nodeList.data(), &LimitedNodeList::nodeAdded, this, &DomainServer::nodeAdded);
connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &DomainServer::nodeKilled);
@ -1133,7 +1137,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
extendedHeaderStream << node->getUUID();
extendedHeaderStream << node->getLocalID();
extendedHeaderStream << node->getPermissions();
extendedHeaderStream << limitedNodeList->getAuthenticatePackets();
auto domainListPackets = NLPacketList::create(PacketType::DomainList, extendedHeader);
// always send the node their own UUID back

View file

@ -62,6 +62,7 @@ Windows.ScrollingWindow {
url: "about:blank"
anchors.fill: parent
focus: true
profile: HFWebEngineProfile;
property string userScriptUrl: ""

View file

@ -299,7 +299,7 @@ Item {
anchors.fill: stackView
id: controllerPrefereneces
objectName: "TabletControllerPreferences"
showCategories: [( (HMD.active) ? "VR Movement" : "Movement"), "Game Controller", "Sixense Controllers", "Perception Neuron", "Leap Motion"]
showCategories: ["VR Movement", "Game Controller", "Sixense Controllers", "Perception Neuron", "Leap Motion"]
categoryProperties: {
"VR Movement" : {
"User real-world height (meters)" : { "anchors.right" : "undefined" },

View file

@ -6619,11 +6619,12 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
LocationScriptingInterface::locationSetter);
bool clientScript = scriptEngine->isClientScript();
scriptEngine->registerFunction("OverlayWindow", clientScript ? QmlWindowClass::constructor : QmlWindowClass::restricted_constructor);
#if !defined(Q_OS_ANDROID)
scriptEngine->registerFunction("OverlayWebWindow", QmlWebWindowClass::constructor);
scriptEngine->registerFunction("OverlayWebWindow", clientScript ? QmlWebWindowClass::constructor : QmlWebWindowClass::restricted_constructor);
#endif
scriptEngine->registerFunction("OverlayWindow", QmlWindowClass::constructor);
scriptEngine->registerFunction("QmlFragment", QmlFragmentClass::constructor);
scriptEngine->registerFunction("QmlFragment", clientScript ? QmlFragmentClass::constructor : QmlFragmentClass::restricted_constructor);
scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("DesktopPreviewProvider", DependencyManager::get<DesktopPreviewProvider>().data());

View file

@ -412,9 +412,6 @@ void AvatarManager::clearOtherAvatars() {
while (avatarIterator != _avatarHash.end()) {
auto avatar = std::static_pointer_cast<Avatar>(avatarIterator.value());
if (avatar != _myAvatar) {
if (avatar->isInScene()) {
avatar->removeFromScene(avatar, scene, transaction);
}
handleRemovedAvatar(avatar);
avatarIterator = _avatarHash.erase(avatarIterator);
} else {

View file

@ -19,6 +19,7 @@
AvatarMotionState::AvatarMotionState(AvatarSharedPointer avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) {
assert(_avatar);
_type = MOTIONSTATE_TYPE_AVATAR;
cacheShapeDiameter();
}
void AvatarMotionState::handleEasyChanges(uint32_t& flags) {
@ -57,9 +58,6 @@ PhysicsMotionType AvatarMotionState::computePhysicsMotionType() const {
const btCollisionShape* AvatarMotionState::computeNewShape() {
ShapeInfo shapeInfo;
std::static_pointer_cast<Avatar>(_avatar)->computeShapeInfo(shapeInfo);
glm::vec3 halfExtents = shapeInfo.getHalfExtents();
halfExtents.y = 0.0f;
_diameter = 2.0f * glm::length(halfExtents);
return getShapeManager()->getShape(shapeInfo);
}
@ -98,6 +96,10 @@ void AvatarMotionState::setWorldTransform(const btTransform& worldTrans) {
btVector3 velocity = glmToBullet(getObjectLinearVelocity()) + (1.0f / SPRING_TIMESCALE) * offsetToTarget;
_body->setLinearVelocity(velocity);
_body->setAngularVelocity(glmToBullet(getObjectAngularVelocity()));
// slam its rotation
btTransform newTransform = worldTrans;
newTransform.setRotation(glmToBullet(getObjectRotation()));
_body->setWorldTransform(newTransform);
}
}
@ -141,7 +143,10 @@ glm::vec3 AvatarMotionState::getObjectLinearVelocity() const {
// virtual
glm::vec3 AvatarMotionState::getObjectAngularVelocity() const {
return _avatar->getWorldAngularVelocity();
// HACK: avatars use a capusle collision shape and their angularVelocity in the local simulation is unimportant.
// Therefore, as optimization toward support for larger crowds we ignore it and return zero.
//return _avatar->getWorldAngularVelocity();
return glm::vec3(0.0f);
}
// virtual
@ -174,3 +179,28 @@ float AvatarMotionState::getMass() const {
return std::static_pointer_cast<Avatar>(_avatar)->computeMass();
}
void AvatarMotionState::cacheShapeDiameter() {
if (_shape) {
// shape is capsuleY
btVector3 aabbMin, aabbMax;
btTransform transform;
transform.setIdentity();
_shape->getAabb(transform, aabbMin, aabbMax);
_diameter = (aabbMax - aabbMin).getX();
} else {
_diameter = 0.0f;
}
}
void AvatarMotionState::setRigidBody(btRigidBody* body) {
ObjectMotionState::setRigidBody(body);
if (_body) {
// remove angular dynamics from this body
_body->setAngularFactor(0.0f);
}
}
void AvatarMotionState::setShape(const btCollisionShape* shape) {
ObjectMotionState::setShape(shape);
cacheShapeDiameter();
}

View file

@ -74,6 +74,10 @@ public:
friend class Avatar;
protected:
void setRigidBody(btRigidBody* body) override;
void setShape(const btCollisionShape* shape) override;
void cacheShapeDiameter();
// the dtor had been made protected to force the compiler to verify that it is only
// ever called by the Avatar class dtor.
~AvatarMotionState();

View file

@ -1135,7 +1135,6 @@ void MyAvatar::saveData() {
settings.setValue("collisionSoundURL", _collisionSoundURL);
settings.setValue("useSnapTurn", _useSnapTurn);
settings.setValue("userHeight", getUserHeight());
settings.setValue("flyingDesktop", getFlyingDesktopPref());
settings.setValue("flyingHMD", getFlyingHMDPref());
settings.endGroup();
@ -1289,7 +1288,6 @@ void MyAvatar::loadData() {
// Flying preferences must be loaded before calling setFlyingEnabled()
Setting::Handle<bool> firstRunVal { Settings::firstRun, true };
setFlyingDesktopPref(firstRunVal.get() ? true : settings.value("flyingDesktop").toBool());
setFlyingHMDPref(firstRunVal.get() ? false : settings.value("flyingHMD").toBool());
setFlyingEnabled(getFlyingEnabled());

View file

@ -26,7 +26,7 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
SINGLETON_DEPENDENCY
/**jsdoc
* The Audio API features tools to help control audio contexts and settings.
* The <code>Audio</code> API provides facilities to interact with audio inputs and outputs and to play sounds.
*
* @namespace Audio
*
@ -35,14 +35,23 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
* @hifi-server-entity
* @hifi-assignment-client
*
* @property {boolean} muted
* @property {boolean} noiseReduction
* @property {number} inputVolume
* @property {number} inputLevel <em>Read-only.</em>
* @property {string} context <em>Read-only.</em>
* @property {} devices <em>Read-only.</em>
* @property {boolean} muted - <code>true</code> if the audio input is muted, otherwise <code>false</code>.
* @property {boolean} noiseReduction - <code>true</code> if noise reduction is enabled, otherwise <code>false</code>. When
* enabled, the input audio signal is blocked (fully attenuated) when it falls below an adaptive threshold set just
* above the noise floor.
* @property {number} inputLevel - The loudness of the audio input, range <code>0.0</code> (no sound) &ndash;
* <code>1.0</code> (the onset of clipping). <em>Read-only.</em>
* @property {number} inputVolume - Adjusts the volume of the input audio; range <code>0.0</code> &ndash; <code>1.0</code>.
* If set to a value, the resulting value depends on the input device: for example, the volume can't be changed on some
* devices, and others might only support values of <code>0.0</code> and <code>1.0</code>.
* @property {boolean} isStereoInput - <code>true</code> if the input audio is being used in stereo, otherwise
* <code>false</code>. Some devices do not support stereo, in which case the value is always <code>false</code>.
* @property {string} context - The current context of the audio: either <code>"Desktop"</code> or <code>"HMD"</code>.
* <em>Read-only.</em>
* @property {object} devices <em>Read-only.</em> <strong>Deprecated:</strong> This property is deprecated and will be
* removed.
*/
Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged)
Q_PROPERTY(bool noiseReduction READ noiseReductionEnabled WRITE enableNoiseReduction NOTIFY noiseReductionChanged)
Q_PROPERTY(float inputVolume READ getInputVolume WRITE setInputVolume NOTIFY inputVolumeChanged)
@ -69,45 +78,91 @@ public:
/**jsdoc
* @function Audio.setInputDevice
* @param {} device
* @param {object} device
* @param {boolean} isHMD
* @deprecated This function is deprecated and will be removed.
*/
Q_INVOKABLE void setInputDevice(const QAudioDeviceInfo& device, bool isHMD);
/**jsdoc
* @function Audio.setOutputDevice
* @param {} device
* @param {object} device
* @param {boolean} isHMD
* @deprecated This function is deprecated and will be removed.
*/
Q_INVOKABLE void setOutputDevice(const QAudioDeviceInfo& device, bool isHMD);
/**jsdoc
* Enable or disable reverberation. Reverberation is done by the client, on the post-mix audio. The reverberation options
* come from either the domain's audio zone if used &mdash; configured on the server &mdash; or as scripted by
* {@link Audio.setReverbOptions|setReverbOptions}.
* @function Audio.setReverb
* @param {boolean} enable
*/
* @param {boolean} enable - <code>true</code> to enable reverberation, <code>false</code> to disable.
* @example <caption>Enable reverberation for a short while.</caption>
* var sound = SoundCache.getSound(Script.resourcesPath() + "sounds/sample.wav");
* var injector;
* var injectorOptions = {
* position: MyAvatar.position
* };
*
* Script.setTimeout(function () {
* print("Reverb OFF");
* Audio.setReverb(false);
* injector = Audio.playSound(sound, injectorOptions);
* }, 1000);
*
* Script.setTimeout(function () {
* var reverbOptions = new AudioEffectOptions();
* reverbOptions.roomSize = 100;
* Audio.setReverbOptions(reverbOptions);
* print("Reverb ON");
* Audio.setReverb(true);
* }, 4000);
*
* Script.setTimeout(function () {
* print("Reverb OFF");
* Audio.setReverb(false);
* }, 8000); */
Q_INVOKABLE void setReverb(bool enable);
/**jsdoc
* Configure reverberation options. Use {@link Audio.setReverb|setReverb} to enable or disable reverberation.
* @function Audio.setReverbOptions
* @param {AudioEffectOptions} options
* @param {AudioEffectOptions} options - The reverberation options.
*/
Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options);
/**jsdoc
* Starts making an audio recording of the audio being played in-world (i.e., not local-only audio) to a file in WAV format.
* @function Audio.startRecording
* @param {string} filename
* @returns {boolean}
* @param {string} filename - The path and name of the file to make the recording in. Should have a <code>.wav</code>
* extension. The file is overwritten if it already exists.
* @returns {boolean} <code>true</code> if the specified file could be opened and audio recording has started, otherwise
* <code>false</code>.
* @example <caption>Make a 10 second audio recording.</caption>
* var filename = File.getTempDir() + "/audio.wav";
* if (Audio.startRecording(filename)) {
* Script.setTimeout(function () {
* Audio.stopRecording();
* print("Audio recording made in: " + filename);
* }, 10000);
*
* } else {
* print("Could not make an audio recording in: " + filename);
* }
*/
Q_INVOKABLE bool startRecording(const QString& filename);
/**jsdoc
* Finish making an audio recording started with {@link Audio.startRecording|startRecording}.
* @function Audio.stopRecording
*/
Q_INVOKABLE void stopRecording();
/**jsdoc
* Check whether an audio recording is currently being made.
* @function Audio.getRecording
* @returns {boolean}
* @returns {boolean} <code>true</code> if an audio recording is currently being made, otherwise <code>false</code>.
*/
Q_INVOKABLE bool getRecording();
@ -116,40 +171,54 @@ signals:
/**jsdoc
* @function Audio.nop
* @returns {Signal}
* @deprecated This signal is deprecated and will be removed.
*/
void nop();
/**jsdoc
* Triggered when the audio input is muted or unmuted.
* @function Audio.mutedChanged
* @param {boolean} isMuted
* @param {boolean} isMuted - <code>true</code> if the audio input is muted, otherwise <code>false</code>.
* @returns {Signal}
* @example <caption>Report when audio input is muted or unmuted</caption>
* Audio.mutedChanged.connect(function (isMuted) {
* print("Audio muted: " + isMuted);
* });
*/
void mutedChanged(bool isMuted);
/**jsdoc
* Triggered when the audio input noise reduction is enabled or disabled.
* @function Audio.noiseReductionChanged
* @param {boolean} isEnabled
* @param {boolean} isEnabled - <code>true</code> if audio input noise reduction is enabled, otherwise <code>false</code>.
* @returns {Signal}
*/
void noiseReductionChanged(bool isEnabled);
/**jsdoc
* Triggered when the input audio volume changes.
* @function Audio.inputVolumeChanged
* @param {number} volume
* @param {number} volume - The requested volume to be applied to the audio input, range <code>0.0</code> &ndash;
* <code>1.0</code>. The resulting value of <code>Audio.inputVolume</code> depends on the capabilities of the device:
* for example, the volume can't be changed on some devices, and others might only support values of <code>0.0</code>
* and <code>1.0</code>.
* @returns {Signal}
*/
void inputVolumeChanged(float volume);
/**jsdoc
* Triggered when the input audio level changes.
* @function Audio.inputLevelChanged
* @param {number} level
* @param {number} level - The loudness of the input audio, range <code>0.0</code> (no sound) &ndash; <code>1.0</code> (the
* onset of clipping).
* @returns {Signal}
*/
void inputLevelChanged(float level);
/**jsdoc
* Triggered when the current context of the audio changes.
* @function Audio.contextChanged
* @param {string} context
* @param {string} context - The current context of the audio: either <code>"Desktop"</code> or <code>"HMD"</code>.
* @returns {Signal}
*/
void contextChanged(const QString& context);
@ -158,7 +227,7 @@ public slots:
/**jsdoc
* @function Audio.onContextChanged
* @returns {Signal}
* @deprecated This function is deprecated and will be removed.
*/
void onContextChanged();

View file

@ -266,42 +266,6 @@ void setupPreferences() {
preferences->addPreference(new SliderPreference(FACE_TRACKING, "Eye Deflection", getter, setter));
}
static const QString MOVEMENT{ "Movement" };
{
static const QString movementsControlChannel = QStringLiteral("Hifi-Advanced-Movement-Disabler");
auto getter = [=]()->bool { return myAvatar->useAdvancedMovementControls(); };
auto setter = [=](bool value) { myAvatar->setUseAdvancedMovementControls(value); };
preferences->addPreference(new CheckPreference(MOVEMENT,
QStringLiteral("Advanced movement for hand controllers"),
getter, setter));
}
{
auto getter = [=]()->int { return myAvatar->getSnapTurn() ? 0 : 1; };
auto setter = [=](int value) { myAvatar->setSnapTurn(value == 0); };
auto preference = new RadioButtonsPreference(MOVEMENT, "Snap turn / Smooth turn", getter, setter);
QStringList items;
items << "Snap turn" << "Smooth turn";
preference->setItems(items);
preferences->addPreference(preference);
}
{
auto getter = [=]()->float { return myAvatar->getUserHeight(); };
auto setter = [=](float value) { myAvatar->setUserHeight(value); };
auto preference = new SpinnerPreference(MOVEMENT, "User real-world height (meters)", getter, setter);
preference->setMin(1.0f);
preference->setMax(2.2f);
preference->setDecimals(3);
preference->setStep(0.001f);
preferences->addPreference(preference);
}
{
auto preference = new ButtonPreference(MOVEMENT, "RESET SENSORS", [] {
qApp->resetSensors();
});
preferences->addPreference(preference);
}
static const QString VR_MOVEMENT{ "VR Movement" };
{
@ -315,7 +279,7 @@ void setupPreferences() {
{
auto getter = [=]()->bool { return myAvatar->getFlyingHMDPref(); };
auto setter = [=](bool value) { myAvatar->setFlyingHMDPref(value); };
preferences->addPreference(new CheckPreference(VR_MOVEMENT, "Flying & jumping", getter, setter));
preferences->addPreference(new CheckPreference(VR_MOVEMENT, "Flying & jumping (HMD)", getter, setter));
}
{
auto getter = [=]()->int { return myAvatar->getSnapTurn() ? 0 : 1; };

View file

@ -59,28 +59,29 @@ static void setOption(QScriptValue arguments, const QString name, float defaultV
}
/**jsdoc
* Reverberation options that can be used to initialize an {@link AudioEffectOptions} object when created.
* @typedef {object} AudioEffectOptions.ReverbOptions
* @property {number} bandwidth
* @property {number} preDelay
* @property {number} lateDelay
* @property {number} reverbTime
* @property {number} earlyDiffusion
* @property {number} lateDiffusion
* @property {number} roomSize
* @property {number} density
* @property {number} bassMult
* @property {number} bassFreq
* @property {number} highGain
* @property {number} highFreq
* @property {number} modRate
* @property {number} modDepth
* @property {number} earlyGain
* @property {number} lateGain
* @property {number} earlyMixLeft
* @property {number} earlyMixRight
* @property {number} lateMixLeft
* @property {number} lateMixRight
* @property {number} wetDryMix
* @property {number} bandwidth=10000 - The corner frequency (Hz) of the low-pass filter at reverb input.
* @property {number} preDelay=20 - The delay (milliseconds) between dry signal and the onset of early reflections.
* @property {number} lateDelay=0 - The delay (milliseconds) between early reflections and the onset of reverb tail.
* @property {number} reverbTime=2 - The time (seconds) for the reverb tail to decay by 60dB, also known as RT60.
* @property {number} earlyDiffusion=100 - Adjusts the buildup of echo density in the early reflections, normally 100%.
* @property {number} lateDiffusion=100 - Adjusts the buildup of echo density in the reverb tail, normally 100%.
* @property {number} roomSize=50 - The apparent room size, from small (0%) to large (100%).
* @property {number} density=100 - Adjusts the echo density in the reverb tail, normally 100%.
* @property {number} bassMult=1.5 - Adjusts the bass-frequency reverb time, as multiple of reverbTime.
* @property {number} bassFreq=250 - The crossover frequency (Hz) for the onset of bassMult.
* @property {number} highGain=-6 - Reduces the high-frequency reverb time, as attenuation (dB).
* @property {number} highFreq=3000 - The crossover frequency (Hz) for the onset of highGain.
* @property {number} modRate=2.3 - The rate of modulation (Hz) of the LFO-modulated delay lines.
* @property {number} modDepth=50 - The depth of modulation (percent) of the LFO-modulated delay lines.
* @property {number} earlyGain=0 - Adjusts the relative level (dB) of the early reflections.
* @property {number} lateGain=0 - Adjusts the relative level (dB) of the reverb tail.
* @property {number} earlyMixLeft=20 - The apparent distance of the source (percent) in the early reflections.
* @property {number} earlyMixRight=20 - The apparent distance of the source (percent) in the early reflections.
* @property {number} lateMixLeft=90 - The apparent distance of the source (percent) in the reverb tail.
* @property {number} lateMixRight=90 - The apparent distance of the source (percent) in the reverb tail.
* @property {number} wetDryMix=50 - Adjusts the wet/dry ratio, from completely dry (0%) to completely wet (100%).
*/
AudioEffectOptions::AudioEffectOptions(QScriptValue arguments) {
setOption(arguments, BANDWIDTH_HANDLE, BANDWIDTH_DEFAULT, _bandwidth);

View file

@ -16,35 +16,39 @@
#include <QtScript/QScriptEngine>
/**jsdoc
* Audio effect options used by the {@link Audio} API.
*
* <p>Create using <code>new AudioEffectOptions(reverbOptions)</code>.</p>
*
* @class AudioEffectOptions
* @param {AudioEffectOptions.ReverbOptions} [reverbOptions=null]
* @param {AudioEffectOptions.ReverbOptions} [reverbOptions=null] - Reverberation options.
*
* @hifi-interface
* @hifi-client-entity
* @hifi-server-entity
* @hifi-assignment-client
*
* @property {number} bandwidth=10000
* @property {number} preDelay=20
* @property {number} lateDelay=0
* @property {number} reverbTime=2
* @property {number} earlyDiffusion=100
* @property {number} lateDiffusion=100
* @property {number} roomSize=50
* @property {number} density=100
* @property {number} bassMult=1.5
* @property {number} bassFreq=250
* @property {number} highGain=-6
* @property {number} highFreq=3000
* @property {number} modRate=2.3
* @property {number} modDepth=50
* @property {number} earlyGain=0
* @property {number} lateGain=0
* @property {number} earlyMixLeft=20
* @property {number} earlyMixRight=20
* @property {number} lateMixLeft=90
* @property {number} lateMixRight=90
* @property {number} wetDryMix=50
* @property {number} bandwidth=10000 - The corner frequency (Hz) of the low-pass filter at reverb input.
* @property {number} preDelay=20 - The delay (milliseconds) between dry signal and the onset of early reflections.
* @property {number} lateDelay=0 - The delay (milliseconds) between early reflections and the onset of reverb tail.
* @property {number} reverbTime=2 - The time (seconds) for the reverb tail to decay by 60dB, also known as RT60.
* @property {number} earlyDiffusion=100 - Adjusts the buildup of echo density in the early reflections, normally 100%.
* @property {number} lateDiffusion=100 - Adjusts the buildup of echo density in the reverb tail, normally 100%.
* @property {number} roomSize=50 - The apparent room size, from small (0%) to large (100%).
* @property {number} density=100 - Adjusts the echo density in the reverb tail, normally 100%.
* @property {number} bassMult=1.5 - Adjusts the bass-frequency reverb time, as multiple of reverbTime.
* @property {number} bassFreq=250 - The crossover frequency (Hz) for the onset of bassMult.
* @property {number} highGain=-6 - Reduces the high-frequency reverb time, as attenuation (dB).
* @property {number} highFreq=3000 - The crossover frequency (Hz) for the onset of highGain.
* @property {number} modRate=2.3 - The rate of modulation (Hz) of the LFO-modulated delay lines.
* @property {number} modDepth=50 - The depth of modulation (percent) of the LFO-modulated delay lines.
* @property {number} earlyGain=0 - Adjusts the relative level (dB) of the early reflections.
* @property {number} lateGain=0 - Adjusts the relative level (dB) of the reverb tail.
* @property {number} earlyMixLeft=20 - The apparent distance of the source (percent) in the early reflections.
* @property {number} earlyMixRight=20 - The apparent distance of the source (percent) in the early reflections.
* @property {number} lateMixLeft=90 - The apparent distance of the source (percent) in the reverb tail.
* @property {number} lateMixRight=90 - The apparent distance of the source (percent) in the reverb tail.
* @property {number} wetDryMix=50 - Adjusts the wet/dry ratio, from completely dry (0%) to completely wet (100%).
*/
class AudioEffectOptions : public QObject {

View file

@ -45,6 +45,23 @@ QScriptValue injectorOptionsToScriptValue(QScriptEngine* engine, const AudioInje
return obj;
}
/**jsdoc
* Configures how an audio injector plays its audio.
* @typedef {object} AudioInjector.AudioInjectorOptions
* @property {Vec3} position=Vec3.ZERO - The position in the domain to play the sound.
* @property {Quat} orientation=Quat.IDENTITY - The orientation in the domain to play the sound in.
* @property {number} volume=1.0 - Playback volume, between <code>0.0</code> and <code>1.0</code>.
* @property {number} pitch=1.0 - Alter the pitch of the sound, within +/- 2 octaves. The value is the relative sample rate to
* resample the sound at, range <code>0.0625</code> &ndash; <code>16.0</code>. A value of <code>0.0625</code> lowers the
* pitch by 2 octaves; <code>1.0</code> is no change in pitch; <code>16.0</code> raises the pitch by 2 octaves.
* @property {boolean} loop=false - If <code>true</code>, the sound is played repeatedly until playback is stopped.
* @property {number} secondOffset=0 - Starts playback from a specified time (seconds) within the sound file, &ge;
* <code>0</code>.
* @property {boolean} localOnly=false - IF <code>true</code>, the sound is played back locally on the client rather than to
* others via the audio mixer.
* @property {boolean} ignorePenumbra=false - <strong>Deprecated:</strong> This property is deprecated and will be
* removed.
*/
void injectorOptionsFromScriptValue(const QScriptValue& object, AudioInjectorOptions& injectorOptions) {
if (!object.isObject()) {
qWarning() << "Audio injector options is not an object.";

View file

@ -79,6 +79,14 @@ private:
typedef QSharedPointer<Sound> SharedSoundPointer;
/**jsdoc
* An audio resource, created by {@link SoundCache.getSound}, to be played back using {@link Audio.playSound}.
* <p>Supported formats:</p>
* <ul>
* <li>WAV: 16-bit uncompressed WAV at any sample rate, with 1 (mono), 2(stereo), or 4 (ambisonic) channels.</li>
* <li>MP3: Mono or stereo, at any sample rate.</li>
* <li>RAW: 48khz 16-bit mono or stereo. Filename must include <code>".stereo"</code> to be interpreted as stereo.</li>
* </ul>
*
* @class SoundObject
*
* @hifi-interface
@ -86,8 +94,9 @@ typedef QSharedPointer<Sound> SharedSoundPointer;
* @hifi-server-entity
* @hifi-assignment-client
*
* @property {boolean} downloaded
* @property {number} duration
* @property {boolean} downloaded - <code>true</code> if the sound has been downloaded and is ready to be played, otherwise
* <code>false</code>.
* @property {number} duration - The duration of the sound, in seconds.
*/
class SoundScriptingInterface : public QObject {
Q_OBJECT
@ -103,6 +112,7 @@ public:
float getDuration() { return _sound->getDuration(); }
/**jsdoc
* Triggered when the sound has been downloaded and is ready to be played.
* @function SoundObject.ready
* @returns {Signal}
*/

View file

@ -48,9 +48,11 @@ public:
SoundCacheScriptingInterface();
/**jsdoc
* Loads the content of an audio file into a {@link SoundObject}, ready for playback by {@link Audio.playSound}.
* @function SoundCache.getSound
* @param {string} url
* @returns {SoundObject}
* @param {string} url - The URL of the audio file to load &mdash; Web, ATP, or file. See {@link SoundObject} for supported
* formats.
* @returns {SoundObject} The sound ready for playback.
*/
Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url);
};

View file

@ -328,9 +328,10 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
if (sourceNode) {
bool verifiedPacket = !PacketTypeEnum::getNonVerifiedPackets().contains(headerType);
bool ignoreVerification = isDomainServer() && PacketTypeEnum::getDomainIgnoredVerificationPackets().contains(headerType);
bool verificationEnabled = !(isDomainServer() && PacketTypeEnum::getDomainIgnoredVerificationPackets().contains(headerType))
&& _useAuthentication;
if (verifiedPacket && !ignoreVerification) {
if (verifiedPacket && verificationEnabled) {
QByteArray packetHeaderHash = NLPacket::verificationHashInHeader(packet);
QByteArray expectedHash;
@ -383,7 +384,7 @@ void LimitedNodeList::fillPacketHeader(const NLPacket& packet, HMACAuth* hmacAut
packet.writeSourceID(getSessionLocalID());
}
if (hmacAuth
if (_useAuthentication && hmacAuth
&& !PacketTypeEnum::getNonSourcedPackets().contains(packet.getType())
&& !PacketTypeEnum::getNonVerifiedPackets().contains(packet.getType())) {
packet.writeVerificationHash(*hmacAuth);

View file

@ -307,6 +307,8 @@ public:
bool isPacketVerifiedWithSource(const udt::Packet& packet, Node* sourceNode = nullptr);
bool isPacketVerified(const udt::Packet& packet) { return isPacketVerifiedWithSource(packet); }
void setAuthenticatePackets(bool useAuthentication) { _useAuthentication = useAuthentication; }
bool getAuthenticatePackets() const { return _useAuthentication; }
static void makeSTUNRequestPacket(char* stunRequestPacket);
@ -394,6 +396,7 @@ protected:
HifiSockAddr _publicSockAddr;
HifiSockAddr _stunSockAddr { STUN_SERVER_HOSTNAME, STUN_SERVER_PORT };
bool _hasTCPCheckedLocalSocket { false };
bool _useAuthentication { true };
PacketReceiver* _packetReceiver;

View file

@ -665,6 +665,10 @@ void NodeList::processDomainServerList(QSharedPointer<ReceivedMessage> message)
NodePermissions newPermissions;
packetStream >> newPermissions;
setPermissions(newPermissions);
// Is packet authentication enabled?
bool isAuthenticated;
packetStream >> isAuthenticated;
setAuthenticatePackets(isAuthenticated);
// pull each node in the packet
while (packetStream.device()->pos() < message->getSize()) {

View file

@ -120,7 +120,7 @@ void ThreadedAssignment::checkInWithDomainServerOrExit() {
if (_numQueuedCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
qCDebug(networking) << "At least" << MAX_SILENT_DOMAIN_SERVER_CHECK_INS << "have been queued without a response from domain-server"
<< "Stopping the current assignment";
setFinished(true);
stop();
} else {
auto nodeList = DependencyManager::get<NodeList>();
QMetaObject::invokeMethod(nodeList.data(), "sendDomainServerCheckIn");
@ -132,5 +132,5 @@ void ThreadedAssignment::checkInWithDomainServerOrExit() {
void ThreadedAssignment::domainSettingsRequestFailed() {
qCDebug(networking) << "Failed to retreive settings object from domain-server. Bailing on assignment.";
setFinished(true);
stop();
}

View file

@ -24,7 +24,6 @@ public:
ThreadedAssignment(ReceivedMessage& message);
~ThreadedAssignment() { stop(); }
void setFinished(bool isFinished);
virtual void aboutToFinish() { };
void addPacketStatsAndSendStatsPacket(QJsonObject statsObject);
@ -43,6 +42,7 @@ signals:
protected:
void commonInit(const QString& targetName, NodeType_t nodeType);
void setFinished(bool isFinished);
bool _isFinished;
QTimer _domainServerTimer;

View file

@ -27,7 +27,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::StunResponse:
return 17;
case PacketType::DomainList:
return static_cast<PacketVersion>(DomainListVersion::GetMachineFingerprintFromUUIDSupport);
return static_cast<PacketVersion>(DomainListVersion::AuthenticationOptional);
case PacketType::EntityAdd:
case PacketType::EntityClone:
case PacketType::EntityEdit:

View file

@ -315,7 +315,8 @@ enum class DomainListVersion : PacketVersion {
PrePermissionsGrid = 18,
PermissionsGrid,
GetUsernameFromUUIDSupport,
GetMachineFingerprintFromUUIDSupport
GetMachineFingerprintFromUUIDSupport,
AuthenticationOptional
};
enum class AudioVersion : PacketVersion {

View file

@ -64,9 +64,9 @@ ShapeManager* ObjectMotionState::getShapeManager() {
}
ObjectMotionState::ObjectMotionState(const btCollisionShape* shape) :
_shape(shape),
_lastKinematicStep(worldSimulationStep)
{
setShape(shape);
}
ObjectMotionState::~ObjectMotionState() {

View file

@ -175,13 +175,13 @@ protected:
virtual void setMotionType(PhysicsMotionType motionType);
void updateCCDConfiguration();
void setRigidBody(btRigidBody* body);
virtual void setRigidBody(btRigidBody* body);
virtual void setShape(const btCollisionShape* shape);
MotionStateType _type { MOTIONSTATE_TYPE_INVALID }; // type of MotionState
PhysicsMotionType _motionType { MOTION_TYPE_STATIC }; // type of motion: KINEMATIC, DYNAMIC, or STATIC
const btCollisionShape* _shape;
const btCollisionShape* _shape { nullptr };
btRigidBody* _body { nullptr };
float _density { 1.0f };

View file

@ -40,7 +40,8 @@ static QSize clampSize(const QSize& qsize, uint32_t maxDimension) {
return fromGlm(clampSize(toGlm(qsize), maxDimension));
}
const QmlContextObjectCallback OffscreenSurface::DEFAULT_CONTEXT_CALLBACK = [](QQmlContext*, QQuickItem*) {};
const QmlContextObjectCallback OffscreenSurface::DEFAULT_CONTEXT_OBJECT_CALLBACK = [](QQmlContext*, QQuickItem*) {};
const QmlContextCallback OffscreenSurface::DEFAULT_CONTEXT_CALLBACK = [](QQmlContext*) {};
void OffscreenSurface::initializeEngine(QQmlEngine* engine) {
}
@ -266,8 +267,8 @@ void OffscreenSurface::load(const QUrl& qmlSource, bool createNewContext, const
loadInternal(qmlSource, createNewContext, nullptr, callback);
}
void OffscreenSurface::loadInNewContext(const QUrl& qmlSource, const QmlContextObjectCallback& callback) {
load(qmlSource, true, callback);
void OffscreenSurface::loadInNewContext(const QUrl& qmlSource, const QmlContextObjectCallback& callback, const QmlContextCallback& contextCallback) {
loadInternal(qmlSource, true, nullptr, callback, contextCallback);
}
void OffscreenSurface::load(const QUrl& qmlSource, const QmlContextObjectCallback& callback) {
@ -281,7 +282,8 @@ void OffscreenSurface::load(const QString& qmlSourceFile, const QmlContextObject
void OffscreenSurface::loadInternal(const QUrl& qmlSource,
bool createNewContext,
QQuickItem* parent,
const QmlContextObjectCallback& callback) {
const QmlContextObjectCallback& callback,
const QmlContextCallback& contextCallback) {
PROFILE_RANGE_EX(app, "OffscreenSurface::loadInternal", 0xffff00ff, 0, { std::make_pair("url", qmlSource.toDisplayString()) });
if (QThread::currentThread() != thread()) {
qFatal("Called load on a non-surface thread");
@ -310,6 +312,7 @@ void OffscreenSurface::loadInternal(const QUrl& qmlSource,
}
auto targetContext = contextForUrl(finalQmlSource, parent, createNewContext);
contextCallback(targetContext);
QQmlComponent* qmlComponent;
{
PROFILE_RANGE(app, "new QQmlComponent");

View file

@ -37,13 +37,15 @@ namespace impl {
class SharedObject;
}
using QmlContextCallback = ::std::function<void(QQmlContext*)>;
using QmlContextObjectCallback = ::std::function<void(QQmlContext*, QQuickItem*)>;
class OffscreenSurface : public QObject {
Q_OBJECT
public:
static const QmlContextObjectCallback DEFAULT_CONTEXT_CALLBACK;
static const QmlContextObjectCallback DEFAULT_CONTEXT_OBJECT_CALLBACK;
static const QmlContextCallback DEFAULT_CONTEXT_CALLBACK;
using TextureAndFence = std::pair<uint32_t, void*>;
using MouseTranslator = std::function<QPoint(const QPointF&)>;
@ -85,10 +87,15 @@ public:
Q_INVOKABLE void load(const QUrl& qmlSource, QQuickItem* parent, const QJSValue& callback);
// For use from C++
Q_INVOKABLE void load(const QUrl& qmlSource, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK);
Q_INVOKABLE void load(const QUrl& qmlSource, bool createNewContext, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK);
Q_INVOKABLE void load(const QString& qmlSourceFile, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK);
Q_INVOKABLE void loadInNewContext(const QUrl& qmlSource, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK);
Q_INVOKABLE void load(const QUrl& qmlSource, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_OBJECT_CALLBACK);
Q_INVOKABLE void load(const QUrl& qmlSource,
bool createNewContext,
const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_OBJECT_CALLBACK);
Q_INVOKABLE void load(const QString& qmlSourceFile,
const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_OBJECT_CALLBACK);
Q_INVOKABLE void loadInNewContext(const QUrl& qmlSource,
const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_OBJECT_CALLBACK,
const QmlContextCallback& contextCallback = DEFAULT_CONTEXT_CALLBACK);
public slots:
virtual void onFocusObjectChanged(QObject* newFocus) {}
@ -103,19 +110,21 @@ protected:
virtual void initializeEngine(QQmlEngine* engine);
virtual void loadInternal(const QUrl& qmlSource,
bool createNewContext,
QQuickItem* parent,
const QmlContextObjectCallback& callback) final;
bool createNewContext,
QQuickItem* parent,
const QmlContextObjectCallback& callback,
const QmlContextCallback& contextCallback = DEFAULT_CONTEXT_CALLBACK) final;
virtual void finishQmlLoad(QQmlComponent* qmlComponent,
QQmlContext* qmlContext,
QQuickItem* parent,
const QmlContextObjectCallback& onQmlLoadedCallback) final;
QQmlContext* qmlContext,
QQuickItem* parent,
const QmlContextObjectCallback& onQmlLoadedCallback) final;
virtual void onRootCreated() {}
virtual void onItemCreated(QQmlContext* context, QQuickItem* newItem) {}
virtual void onRootContextCreated(QQmlContext* qmlContext) {}
virtual QQmlContext* contextForUrl(const QUrl& qmlSource, QQuickItem* parent, bool forceNewContext);
private:
MouseTranslator _mouseTranslator{ [](const QPointF& p) { return p.toPoint(); } };
friend class hifi::qml::impl::SharedObject;

View file

@ -23,6 +23,7 @@ class AudioScriptingInterface : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
// JSDoc for property is in Audio.h.
Q_PROPERTY(bool isStereoInput READ isStereoInput WRITE setStereoInput NOTIFY isStereoInputChanged)
public:
@ -35,91 +36,121 @@ protected:
// these methods are protected to stop C++ callers from calling, but invokable from script
/**jsdoc
* Starts playing &mdash; "injecting" &mdash; the content of an audio file. The sound is played globally (sent to the audio
* mixer) so that everyone hears it, unless the <code>injectorOptions</code> has <code>localOnly</code> set to
* <code>true</code> in which case only the client hears the sound played. No sound is played if sent to the audio mixer
* but the client is not connected to an audio mixer. The {@link AudioInjector} object returned by the function can be used
* to control the playback and get information about its current state.
* @function Audio.playSound
* @param {} sound
* @param {} [injectorOptions=null]
* @returns {object}
* @param {SoundObject} sound - The content of an audio file, loaded using {@link SoundCache.getSound}. See
* {@link SoundObject} for supported formats.
* @param {AudioInjector.AudioInjectorOptions} [injectorOptions={}] - Audio injector configuration.
* @returns {AudioInjector} The audio injector that plays the audio file.
* @example <caption>Play a sound.</caption>
* var sound = SoundCache.getSound(Script.resourcesPath() + "sounds/sample.wav");
* var injector;
* var injectorOptions = {
* position: MyAvatar.position
* };
*
* Script.setTimeout(function () { // Give the sound time to load.
* injector = Audio.playSound(sound, injectorOptions);
* }, 1000);
*/
Q_INVOKABLE ScriptAudioInjector* playSound(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions());
/**jsdoc
* Start playing the content of an audio file, locally (isn't sent to the audio mixer). This is the same as calling
* {@link Audio.playSound} with {@link AudioInjector.AudioInjectorOptions} <code>localOnly</code> set <code>true</code> and
* the specified <code>position</code>.
* @function Audio.playSystemSound
* @param {} sound
* @param {} position
* @returns {object}
* @param {SoundObject} sound - The content of an audio file, loaded using {@link SoundCache.getSound}. See
* {@link SoundObject} for supported formats.
* @param {Vec3} position - The position in the domain to play the sound.
* @returns {AudioInjector} The audio injector that plays the audio file.
*/
// FIXME: there is no way to play a positionless sound
Q_INVOKABLE ScriptAudioInjector* playSystemSound(SharedSoundPointer sound, const QVector3D& position);
/**jsdoc
* Set whether or not the audio input should be used in stereo. If the audio input does not support stereo then setting a
* value of <code>true</code> has no effect.
* @function Audio.setStereoInput
* @param {boolean} stereo
* @param {boolean} stereo - <code>true</code> if the audio input should be used in stereo, otherwise <code>false</code>.
*/
Q_INVOKABLE void setStereoInput(bool stereo);
/**jsdoc
* Get whether or not the audio input is used in stereo.
* @function Audio.isStereoInput
* @returns {boolean}
* @returns {boolean} <code>true</code> if the audio input is used in stereo, otherwise <code>false</code>.
*/
Q_INVOKABLE bool isStereoInput();
signals:
/**jsdoc
* The client has been muted by the mixer.
* Triggered when the client is muted by the mixer because their loudness value for the noise background has reached the
* threshold set for the domain in the server settings.
* @function Audio.mutedByMixer
* @returns {Signal}
*/
void mutedByMixer();
/**jsdoc
* The entire environment has been muted by the mixer.
* Triggered when the client is muted by the mixer because they're within a certain radius (50m) of someone who requested
* the mute through Developer &gt; Audio &gt; Mute Environment.
* @function Audio.environmentMuted
* @returns {Signal}
*/
void environmentMuted();
/**jsdoc
* The client has received its first packet from the audio mixer.
* Triggered when the client receives its first packet from the audio mixer.
* @function Audio.receivedFirstPacket
* @returns {Signal}
*/
void receivedFirstPacket();
/**jsdoc
* The client has been disconnected from the audio mixer.
* Triggered when the client is disconnected from the audio mixer.
* @function Audio.disconnected
* @returns {Signal}
*/
void disconnected();
/**jsdoc
* The noise gate has opened.
* Triggered when the noise gate is opened: the input audio signal is no longer blocked (fully attenuated) because it has
* risen above an adaptive threshold set just above the noise floor. Only occurs if <code>Audio.noiseReduction</code> is
* <code>true</code>.
* @function Audio.noiseGateOpened
* @returns {Signal}
*/
void noiseGateOpened();
/**jsdoc
* The noise gate has closed.
* Triggered when the noise gate is closed: the input audio signal is blocked (fully attenuated) because it has fallen
* below an adaptive threshold set just above the noise floor. Only occurs if <code>Audio.noiseReduction</code> is
* <code>true</code>.
* @function Audio.noiseGateClosed
* @returns {Signal}
*/
void noiseGateClosed();
/**jsdoc
* A frame of mic input audio has been received and processed.
* Triggered when a frame of audio input is processed.
* @function Audio.inputReceived
* @param {} inputSamples
* @param {Int16Array} inputSamples - The audio input processed.
* @returns {Signal}
*/
void inputReceived(const QByteArray& inputSamples);
/**jsdoc
* @function Audio.isStereoInputChanged
* @param {boolean} isStereo
* @returns {Signal}
*/
* Triggered when the input audio use changes between mono and stereo.
* @function Audio.isStereoInputChanged
* @param {boolean} isStereo - <code>true</code> if the input audio is stereo, otherwise <code>false</code>.
* @returns {Signal}
*/
void isStereoInputChanged(bool isStereo);
private:

View file

@ -16,6 +16,22 @@
#include <AudioInjector.h>
/**jsdoc
* Plays &mdash; "injects" &mdash; the content of an audio file. Used in the {@link Audio} API.
*
* @class AudioInjector
*
* @hifi-interface
* @hifi-client-entity
* @hifi-server-entity
* @hifi-assignment-client
*
* @property {boolean} playing - <code>true</code> if the audio is currently playing, otherwise <code>false</code>.
* <em>Read-only.</em>
* @property {number} loudness - The loudness in the last frame of audio, range <code>0.0</code> &ndash; <code>1.0</code>.
* <em>Read-only.</em>
* @property {AudioInjector.AudioInjectorOptions} options - Configures how the injector plays the audio.
*/
class ScriptAudioInjector : public QObject {
Q_OBJECT
@ -26,19 +42,103 @@ public:
ScriptAudioInjector(const AudioInjectorPointer& injector);
~ScriptAudioInjector();
public slots:
/**jsdoc
* Stop current playback, if any, and start playing from the beginning.
* @function AudioInjector.restart
*/
void restart() { _injector->restart(); }
/**jsdoc
* Stop audio playback.
* @function AudioInjector.stop
* @example <caption>Stop playing a sound before it finishes.</caption>
* var sound = SoundCache.getSound(Script.resourcesPath() + "sounds/sample.wav");
* var injector;
* var injectorOptions = {
* position: MyAvatar.position
* };
*
* Script.setTimeout(function () { // Give the sound time to load.
* injector = Audio.playSound(sound, injectorOptions);
* }, 1000);
*
* Script.setTimeout(function () {
* injector.stop();
* }, 2000);
*/
void stop() { _injector->stop(); }
/**jsdoc
* Get the current configuration of the audio injector.
* @function AudioInjector.getOptions
* @returns {AudioInjector.AudioInjectorOptions} Configuration of how the injector plays the audio.
*/
const AudioInjectorOptions& getOptions() const { return _injector->getOptions(); }
/**jsdoc
* Configure how the injector plays the audio.
* @function AudioInjector.setOptions
* @param {AudioInjector.AudioInjectorOptions} options - Configuration of how the injector plays the audio.
*/
void setOptions(const AudioInjectorOptions& options) { _injector->setOptions(options); }
/**jsdoc
* Get the loudness of the most recent frame of audio played.
* @function AudioInjector.getLoudness
* @returns {number} The loudness of the most recent frame of audio played, range <code>0.0</code> &ndash; <code>1.0</code>.
*/
float getLoudness() const { return _injector->getLoudness(); }
/**jsdoc
* Get whether or not the audio is currently playing.
* @function AudioInjector.isPlaying
* @returns {boolean} <code>true</code> if the audio is currently playing, otherwise <code>false</code>.
* @example <caption>See if a sound is playing.</caption>
* var sound = SoundCache.getSound(Script.resourcesPath() + "sounds/sample.wav");
* var injector;
* var injectorOptions = {
* position: MyAvatar.position
* };
*
* Script.setTimeout(function () { // Give the sound time to load.
* injector = Audio.playSound(sound, injectorOptions);
* }, 1000);
*
* Script.setTimeout(function () {
* print("Sound is playing: " + injector.isPlaying());
* }, 2000);
*/
bool isPlaying() const { return _injector->isPlaying(); }
signals:
/**jsdoc
* Triggered when the audio has finished playing.
* @function AudioInjector.finished
* @returns {Signal}
* @example <caption>Report when a sound has finished playing.</caption>
* var sound = SoundCache.getSound(Script.resourcesPath() + "sounds/sample.wav");
* var injector;
* var injectorOptions = {
* position: MyAvatar.position
* };
*
* Script.setTimeout(function () { // Give the sound time to load.
* injector = Audio.playSound(sound, injectorOptions);
* injector.finished.connect(function () {
* print("Finished playing sound");
* });
* }, 1000);
*/
void finished();
protected slots:
/**jsdoc
* Stop audio playback. (Synonym of {@link AudioInjector.stop|stop}.)
* @function AudioInjector.stopInjectorImmediately
*/
void stopInjectorImmediately();
private:
AudioInjectorPointer _injector;

View file

@ -59,7 +59,11 @@ const int32_t BULLET_COLLISION_MASK_KINEMATIC = BULLET_COLLISION_MASK_STATIC;
// MY_AVATAR does not collide with itself
const int32_t BULLET_COLLISION_MASK_MY_AVATAR = ~(BULLET_COLLISION_GROUP_COLLISIONLESS | BULLET_COLLISION_GROUP_MY_AVATAR);
const int32_t BULLET_COLLISION_MASK_OTHER_AVATAR = BULLET_COLLISION_MASK_DEFAULT;
// OTHER_AVATARs are dynamic, but are slammed to whatever the avatar-mixer says, which means
// their motion can't actually be affected by the local physics simulation -- we rely on the remote simulation
// to move its avatar around correctly and to communicate its motion through the avatar-mixer.
// Therefore, they only need to collide against things that can be affected by their motion: dynamic and MyAvatar
const int32_t BULLET_COLLISION_MASK_OTHER_AVATAR = BULLET_COLLISION_GROUP_DYNAMIC | BULLET_COLLISION_GROUP_MY_AVATAR;
// COLLISIONLESS gets an empty mask.
const int32_t BULLET_COLLISION_MASK_COLLISIONLESS = 0;

View file

@ -20,10 +20,10 @@
std::mutex QmlFragmentClass::_mutex;
std::map<QString, QScriptValue> QmlFragmentClass::_fragments;
QmlFragmentClass::QmlFragmentClass(QString id) : qml(id) { }
QmlFragmentClass::QmlFragmentClass(bool restricted, QString id) : QmlWindowClass(restricted), qml(id) { }
// Method called by Qt scripts to create a new bottom menu bar in Android
QScriptValue QmlFragmentClass::constructor(QScriptContext* context, QScriptEngine* engine) {
QScriptValue QmlFragmentClass::internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted) {
std::lock_guard<std::mutex> guard(_mutex);
auto qml = context->argument(0).toVariant().toMap().value("qml");
@ -41,7 +41,7 @@ QScriptValue QmlFragmentClass::constructor(QScriptContext* context, QScriptEngin
auto properties = parseArguments(context);
auto offscreenUi = DependencyManager::get<OffscreenUi>();
QmlFragmentClass* retVal = new QmlFragmentClass(qml.toString());
QmlFragmentClass* retVal = new QmlFragmentClass(restricted, qml.toString());
Q_ASSERT(retVal);
if (QThread::currentThread() != qApp->thread()) {
retVal->moveToThread(qApp->thread());

View file

@ -13,9 +13,19 @@
class QmlFragmentClass : public QmlWindowClass {
Q_OBJECT
private:
static QScriptValue internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted);
public:
QmlFragmentClass(QString id);
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine) {
return internal_constructor(context, engine, false);
}
static QScriptValue restricted_constructor(QScriptContext* context, QScriptEngine* engine ){
return internal_constructor(context, engine, true);
}
QmlFragmentClass(bool restricted, QString id);
/**jsdoc
* Creates a new button, adds it to this and returns it.

View file

@ -20,10 +20,10 @@ static const char* const URL_PROPERTY = "source";
static const char* const SCRIPT_PROPERTY = "scriptUrl";
// Method called by Qt scripts to create a new web window in the overlay
QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
QScriptValue QmlWebWindowClass::internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted) {
auto properties = parseArguments(context);
auto offscreenUi = DependencyManager::get<OffscreenUi>();
QmlWebWindowClass* retVal = new QmlWebWindowClass();
QmlWebWindowClass* retVal = new QmlWebWindowClass(restricted);
Q_ASSERT(retVal);
if (QThread::currentThread() != qApp->thread()) {
retVal->moveToThread(qApp->thread());

View file

@ -57,8 +57,18 @@ class QmlWebWindowClass : public QmlWindowClass {
Q_OBJECT
Q_PROPERTY(QString url READ getURL CONSTANT)
private:
static QScriptValue internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted);
public:
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
QmlWebWindowClass(bool restricted) : QmlWindowClass(restricted) {}
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine) {
return internal_constructor(context, engine, false);
}
static QScriptValue restricted_constructor(QScriptContext* context, QScriptEngine* engine ){
return internal_constructor(context, engine, true);
}
public slots:

View file

@ -25,6 +25,8 @@
#include <shared/QtHelpers.h>
#include "OffscreenUi.h"
#include "ui/types/HFWebEngineProfile.h"
#include "ui/types/FileTypeProfile.h"
static const char* const SOURCE_PROPERTY = "source";
static const char* const TITLE_PROPERTY = "title";
@ -68,10 +70,10 @@ QVariantMap QmlWindowClass::parseArguments(QScriptContext* context) {
// Method called by Qt scripts to create a new web window in the overlay
QScriptValue QmlWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
QScriptValue QmlWindowClass::internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted) {
auto properties = parseArguments(context);
auto offscreenUi = DependencyManager::get<OffscreenUi>();
QmlWindowClass* retVal = new QmlWindowClass();
QmlWindowClass* retVal = new QmlWindowClass(restricted);
Q_ASSERT(retVal);
if (QThread::currentThread() != qApp->thread()) {
retVal->moveToThread(qApp->thread());
@ -83,7 +85,7 @@ QScriptValue QmlWindowClass::constructor(QScriptContext* context, QScriptEngine*
return engine->newQObject(retVal);
}
QmlWindowClass::QmlWindowClass() {
QmlWindowClass::QmlWindowClass(bool restricted) : _restricted(restricted) {
}
@ -99,8 +101,7 @@ void QmlWindowClass::initQml(QVariantMap properties) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
_source = properties[SOURCE_PROPERTY].toString();
// Build the event bridge and wrapper on the main thread
offscreenUi->loadInNewContext(qmlSource(), [&](QQmlContext* context, QObject* object) {
auto objectInitLambda = [&](QQmlContext* context, QObject* object) {
_qmlWindow = object;
context->setContextProperty(EVENT_BRIDGE_PROPERTY, this);
context->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
@ -128,7 +129,24 @@ void QmlWindowClass::initQml(QVariantMap properties) {
if (metaObject->indexOfSignal("moved") >= 0)
connect(_qmlWindow, SIGNAL(moved(QVector2D)), this, SLOT(hasMoved(QVector2D)), Qt::QueuedConnection);
connect(_qmlWindow, SIGNAL(windowClosed()), this, SLOT(hasClosed()), Qt::QueuedConnection);
});
};
auto contextInitLambda = [&](QQmlContext* context) {
#if !defined(Q_OS_ANDROID)
// If the restricted flag is on, override the FileTypeProfile and HFWebEngineProfile objects in the
// QML surface root context with local ones
qDebug() << "Context initialization lambda";
if (_restricted) {
qDebug() << "Restricting web content";
ContextAwareProfile::restrictContext(context);
FileTypeProfile::registerWithContext(context);
HFWebEngineProfile::registerWithContext(context);
}
#endif
};
// Build the event bridge and wrapper on the main thread
offscreenUi->loadInNewContext(qmlSource(), objectInitLambda, contextInitLambda);
Q_ASSERT(_qmlWindow);
Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow.data()));

View file

@ -38,9 +38,18 @@ class QmlWindowClass : public QObject {
Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize NOTIFY sizeChanged)
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged)
private:
static QScriptValue internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted);
public:
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
QmlWindowClass();
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine) {
return internal_constructor(context, engine, false);
}
static QScriptValue restricted_constructor(QScriptContext* context, QScriptEngine* engine ){
return internal_constructor(context, engine, true);
}
QmlWindowClass(bool restricted);
~QmlWindowClass();
/**jsdoc
@ -51,6 +60,8 @@ public:
QQuickItem* asQuickItem() const;
public slots:
/**jsdoc
@ -250,10 +261,12 @@ protected:
QPointer<QObject> _qmlWindow;
QString _source;
const bool _restricted;
private:
// QmlWindow content may include WebView requiring EventBridge.
void setKeyboardRaised(QObject* object, bool raised, bool numeric = false);
};
#endif

View file

@ -265,19 +265,6 @@ void OffscreenQmlSurface::initializeEngine(QQmlEngine* engine) {
if (!javaScriptToInject.isEmpty()) {
rootContext->setContextProperty("eventBridgeJavaScriptToInject", QVariant(javaScriptToInject));
}
#if !defined(Q_OS_ANDROID)
rootContext->setContextProperty("FileTypeProfile", new FileTypeProfile(rootContext));
rootContext->setContextProperty("HFWebEngineProfile", new HFWebEngineProfile(rootContext));
{
PROFILE_RANGE(startup, "FileTypeProfile");
rootContext->setContextProperty("FileTypeProfile", new FileTypeProfile(rootContext));
}
{
PROFILE_RANGE(startup, "HFWebEngineProfile");
rootContext->setContextProperty("HFWebEngineProfile", new HFWebEngineProfile(rootContext));
}
#endif
rootContext->setContextProperty("Paths", DependencyManager::get<PathUtils>().data());
rootContext->setContextProperty("Tablet", DependencyManager::get<TabletScriptingInterface>().data());
rootContext->setContextProperty("Toolbars", DependencyManager::get<ToolbarScriptingInterface>().data());
@ -300,6 +287,17 @@ void OffscreenQmlSurface::onRootContextCreated(QQmlContext* qmlContext) {
// FIXME Compatibility mechanism for existing HTML and JS that uses eventBridgeWrapper
// Find a way to flag older scripts using this mechanism and wanr that this is deprecated
qmlContext->setContextProperty("eventBridgeWrapper", new EventBridgeWrapper(this, qmlContext));
#if !defined(Q_OS_ANDROID)
{
PROFILE_RANGE(startup, "FileTypeProfile");
FileTypeProfile::registerWithContext(qmlContext);
}
{
PROFILE_RANGE(startup, "HFWebEngineProfile");
HFWebEngineProfile::registerWithContext(qmlContext);
}
#endif
}
QQmlContext* OffscreenQmlSurface::contextForUrl(const QUrl& qmlSource, QQuickItem* parent, bool forceNewContext) {

View file

@ -334,6 +334,8 @@ static const char* VRMENU_SOURCE_URL = "hifi/tablet/TabletMenu.qml";
class TabletRootWindow : public QmlWindowClass {
virtual QString qmlSource() const override { return "hifi/tablet/WindowRoot.qml"; }
public:
TabletRootWindow() : QmlWindowClass(false) {}
};
TabletProxy::TabletProxy(QObject* parent, const QString& name) : QObject(parent), _name(name) {

View file

@ -0,0 +1,32 @@
//
// FileTypeProfile.cpp
// interface/src/networking
//
// Created by Kunal Gosar on 2017-03-10.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "ContextAwareProfile.h"
#if !defined(Q_OS_ANDROID)
#include <QtQml/QQmlContext>
static const QString RESTRICTED_FLAG_PROPERTY = "RestrictFileAccess";
ContextAwareProfile::ContextAwareProfile(QQmlContext* parent) :
QQuickWebEngineProfile(parent), _context(parent) { }
void ContextAwareProfile::restrictContext(QQmlContext* context) {
context->setContextProperty(RESTRICTED_FLAG_PROPERTY, true);
}
bool ContextAwareProfile::isRestricted(QQmlContext* context) {
return context->contextProperty(RESTRICTED_FLAG_PROPERTY).toBool();
}
#endif

View file

@ -0,0 +1,42 @@
//
// Created by Bradley Austin Davis on 2018/07/27
// Copyright 2013-2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#ifndef hifi_ContextAwareProfile_h
#define hifi_ContextAwareProfile_h
#include <QtCore/QtGlobal>
#if !defined(Q_OS_ANDROID)
#include <QtWebEngine/QQuickWebEngineProfile>
#include <QtWebEngineCore/QWebEngineUrlRequestInterceptor>
class QQmlContext;
class ContextAwareProfile : public QQuickWebEngineProfile {
public:
static void restrictContext(QQmlContext* context);
static bool isRestricted(QQmlContext* context);
QQmlContext* getContext() const { return _context; }
protected:
class RequestInterceptor : public QWebEngineUrlRequestInterceptor {
public:
RequestInterceptor(ContextAwareProfile* parent) : QWebEngineUrlRequestInterceptor(parent), _profile(parent) {}
QQmlContext* getContext() const { return _profile->getContext(); }
protected:
ContextAwareProfile* _profile;
};
ContextAwareProfile(QQmlContext* parent);
QQmlContext* _context;
};
#endif
#endif // hifi_FileTypeProfile_h

View file

@ -11,18 +11,31 @@
#include "FileTypeProfile.h"
#include "FileTypeRequestInterceptor.h"
#include <QtQml/QQmlContext>
#include "RequestFilters.h"
#if !defined(Q_OS_ANDROID)
static const QString QML_WEB_ENGINE_STORAGE_NAME = "qmlWebEngine";
FileTypeProfile::FileTypeProfile(QObject* parent) :
QQuickWebEngineProfile(parent)
FileTypeProfile::FileTypeProfile(QQmlContext* parent) :
ContextAwareProfile(parent)
{
static const QString WEB_ENGINE_USER_AGENT = "Chrome/48.0 (HighFidelityInterface)";
setHttpUserAgent(WEB_ENGINE_USER_AGENT);
auto requestInterceptor = new FileTypeRequestInterceptor(this);
auto requestInterceptor = new RequestInterceptor(this);
setRequestInterceptor(requestInterceptor);
}
void FileTypeProfile::RequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
RequestFilters::interceptHFWebEngineRequest(info, getContext());
RequestFilters::interceptFileType(info, getContext());
}
void FileTypeProfile::registerWithContext(QQmlContext* context) {
context->setContextProperty("FileTypeProfile", new FileTypeProfile(context));
}
#endif

View file

@ -17,12 +17,23 @@
#include <QtCore/QtGlobal>
#if !defined(Q_OS_ANDROID)
#include <QtWebEngine/QQuickWebEngineProfile>
#include "ContextAwareProfile.h"
class FileTypeProfile : public ContextAwareProfile {
using Parent = ContextAwareProfile;
class FileTypeProfile : public QQuickWebEngineProfile {
public:
FileTypeProfile(QObject* parent = Q_NULLPTR);
static void registerWithContext(QQmlContext* parent);
protected:
FileTypeProfile(QQmlContext* parent);
class RequestInterceptor : public Parent::RequestInterceptor {
public:
RequestInterceptor(ContextAwareProfile* parent) : Parent::RequestInterceptor(parent) {}
void interceptRequest(QWebEngineUrlRequestInfo& info) override;
};
};
#endif
#endif // hifi_FileTypeProfile_h

View file

@ -1,25 +0,0 @@
//
// FileTypeRequestInterceptor.cpp
// interface/src/networking
//
// Created by Kunal Gosar on 2017-03-10.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "FileTypeRequestInterceptor.h"
#include <QtCore/QDebug>
#include "RequestFilters.h"
#if !defined(Q_OS_ANDROID)
void FileTypeRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
RequestFilters::interceptHFWebEngineRequest(info);
RequestFilters::interceptFileType(info);
}
#endif

View file

@ -1,30 +0,0 @@
//
// FileTypeRequestInterceptor.h
// interface/src/networking
//
// Created by Kunal Gosar on 2017-03-10.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#ifndef hifi_FileTypeRequestInterceptor_h
#define hifi_FileTypeRequestInterceptor_h
#include <QtCore/QtGlobal>
#if !defined(Q_OS_ANDROID)
#include <QWebEngineUrlRequestInterceptor>
class FileTypeRequestInterceptor : public QWebEngineUrlRequestInterceptor {
public:
FileTypeRequestInterceptor(QObject* parent) : QWebEngineUrlRequestInterceptor(parent) {};
virtual void interceptRequest(QWebEngineUrlRequestInfo& info) override;
};
#endif
#endif // hifi_FileTypeRequestInterceptor_h

View file

@ -1,23 +0,0 @@
//
// HFTabletWebEngineProfile.h
// interface/src/networking
//
// Created by Dante Ruiz on 2017-03-31.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_HFTabletWebEngineProfile_h
#define hifi_HFTabletWebEngineProfile_h
#include <QtWebEngine/QQuickWebEngineProfile>
class HFTabletWebEngineProfile : public QQuickWebEngineProfile {
public:
HFTabletWebEngineProfile(QObject* parent = Q_NULLPTR);
};
#endif // hifi_HFTabletWebEngineProfile_h

View file

@ -1,30 +0,0 @@
//
// HFTabletWebEngineRequestInterceptor.h
// interface/src/networking
//
// Created by Dante Ruiz on 2017-3-31.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_HFTabletWebEngineRequestInterceptor_h
#define hifi_HFTabletWebEngineRequestInterceptor_h
#if !defined(Q_OS_ANDROID)
#include <QtCore/QObject>
#include <QWebEngineUrlRequestInterceptor>
class HFTabletWebEngineRequestInterceptor
: public QWebEngineUrlRequestInterceptor
{
public:
HFTabletWebEngineRequestInterceptor(QObject* parent)
: QWebEngineUrlRequestInterceptor(parent)
{};
virtual void interceptRequest(QWebEngineUrlRequestInfo& info) override;
};
#endif
#endif // hifi_HFWebEngineRequestInterceptor_h

View file

@ -11,20 +11,28 @@
#include "HFWebEngineProfile.h"
#include "HFWebEngineRequestInterceptor.h"
#include <QtQml/QQmlContext>
#include "RequestFilters.h"
#if !defined(Q_OS_ANDROID)
static const QString QML_WEB_ENGINE_STORAGE_NAME = "qmlWebEngine";
HFWebEngineProfile::HFWebEngineProfile(QObject* parent) :
QQuickWebEngineProfile(parent)
HFWebEngineProfile::HFWebEngineProfile(QQmlContext* parent) : Parent(parent)
{
setStorageName(QML_WEB_ENGINE_STORAGE_NAME);
// we use the HFWebEngineRequestInterceptor to make sure that web requests are authenticated for the interface user
auto requestInterceptor = new HFWebEngineRequestInterceptor(this);
setRequestInterceptor(requestInterceptor);
setRequestInterceptor(new RequestInterceptor(this));
}
#endif
void HFWebEngineProfile::RequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
RequestFilters::interceptHFWebEngineRequest(info, getContext());
}
void HFWebEngineProfile::registerWithContext(QQmlContext* context) {
context->setContextProperty("HFWebEngineProfile", new HFWebEngineProfile(context));
}
#endif

View file

@ -14,15 +14,24 @@
#ifndef hifi_HFWebEngineProfile_h
#define hifi_HFWebEngineProfile_h
#include <QtCore/QtGlobal>
#include "ContextAwareProfile.h"
#if !defined(Q_OS_ANDROID)
#include <QtWebEngine/QQuickWebEngineProfile>
class HFWebEngineProfile : public QQuickWebEngineProfile {
class HFWebEngineProfile : public ContextAwareProfile {
using Parent = ContextAwareProfile;
public:
HFWebEngineProfile(QObject* parent = Q_NULLPTR);
static void registerWithContext(QQmlContext* parent);
protected:
HFWebEngineProfile(QQmlContext* parent);
class RequestInterceptor : public Parent::RequestInterceptor {
public:
RequestInterceptor(ContextAwareProfile* parent) : Parent::RequestInterceptor(parent) {}
void interceptRequest(QWebEngineUrlRequestInfo& info) override;
};
};
#endif
#endif // hifi_HFWebEngineProfile_h

View file

@ -1,25 +0,0 @@
//
// HFWebEngineRequestInterceptor.cpp
// interface/src/networking
//
// Created by Stephen Birarda on 2016-10-14.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "HFWebEngineRequestInterceptor.h"
#include <QtCore/QDebug>
#include "AccountManager.h"
#include "RequestFilters.h"
#if !defined(Q_OS_ANDROID)
void HFWebEngineRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
RequestFilters::interceptHFWebEngineRequest(info);
}
#endif

View file

@ -1,30 +0,0 @@
//
// HFWebEngineRequestInterceptor.h
// interface/src/networking
//
// Created by Stephen Birarda on 2016-10-14.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#ifndef hifi_HFWebEngineRequestInterceptor_h
#define hifi_HFWebEngineRequestInterceptor_h
#include <QtCore/QtGlobal>
#if !defined(Q_OS_ANDROID)
#include <QWebEngineUrlRequestInterceptor>
class HFWebEngineRequestInterceptor : public QWebEngineUrlRequestInterceptor {
public:
HFWebEngineRequestInterceptor(QObject* parent) : QWebEngineUrlRequestInterceptor(parent) {};
virtual void interceptRequest(QWebEngineUrlRequestInfo& info) override;
};
#endif
#endif // hifi_HFWebEngineRequestInterceptor_h

View file

@ -10,12 +10,15 @@
//
#include "RequestFilters.h"
#include "NetworkingConstants.h"
#include <QtCore/QDebug>
#include <SettingHandle.h>
#include <QtCore/QFileInfo>
#include "AccountManager.h"
#include <SettingHandle.h>
#include <NetworkingConstants.h>
#include <AccountManager.h>
#include "ContextAwareProfile.h"
#if !defined(Q_OS_ANDROID)
@ -42,9 +45,29 @@ namespace {
return filename.endsWith(".json", Qt::CaseInsensitive);
}
bool blockLocalFiles(QWebEngineUrlRequestInfo& info) {
auto requestUrl = info.requestUrl();
if (!requestUrl.isLocalFile()) {
// Not a local file, do not block
return false;
}
// We can potentially add whitelisting logic or development environment variables that
// will allow people to override this setting on a per-client basis here.
QString targetFilePath = QFileInfo(requestUrl.toLocalFile()).canonicalFilePath();
// If we get here, we've determined it's a local file and we have no reason not to block it
qWarning() << "Blocking web access to local file path" << targetFilePath;
info.block(true);
return true;
}
}
void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info) {
void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info, QQmlContext* context) {
if (ContextAwareProfile::isRestricted(context) && blockLocalFiles(info)) {
return;
}
// check if this is a request to a highfidelity URL
bool isAuthable = isAuthableHighFidelityURL(info.requestUrl());
if (isAuthable) {
@ -71,7 +94,7 @@ void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info)
info.setHttpHeader(USER_AGENT.toLocal8Bit(), tokenString.toLocal8Bit());
}
void RequestFilters::interceptFileType(QWebEngineUrlRequestInfo& info) {
void RequestFilters::interceptFileType(QWebEngineUrlRequestInfo& info, QQmlContext* context) {
QString filename = info.requestUrl().fileName();
if (isScript(filename) || isJSON(filename)) {
static const QString CONTENT_HEADER = "Accept";
@ -79,4 +102,4 @@ void RequestFilters::interceptFileType(QWebEngineUrlRequestInfo& info) {
info.setHttpHeader(CONTENT_HEADER.toLocal8Bit(), TYPE_VALUE.toLocal8Bit());
}
}
#endif
#endif

View file

@ -20,10 +20,12 @@
#include <QObject>
#include <QWebEngineUrlRequestInfo>
class QQmlContext;
class RequestFilters : public QObject {
public:
static void interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info);
static void interceptFileType(QWebEngineUrlRequestInfo& info);
static void interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info, QQmlContext* context);
static void interceptFileType(QWebEngineUrlRequestInfo& info, QQmlContext* context);
};
#endif