HTML - Mensagens na Web

-

Tipos de Mensagens Web

Mensagens de Canal

As Mensagens de Canal permitem que dois ou mais contextos de navegação (janelas, abas ou iframes) se comuniquem, mesmo que estejam em origens diferentes. Elas usam um objeto de canal de mensagem para estabelecer uma conexão entre os contextos e enviar mensagens entre eles.

As Mensagens de Canal são úteis quando você precisa estabelecer uma conexão direta e bidirecional entre contextos. Por exemplo, você pode usá-las para permitir que uma janela pai controle um iframe ou para que duas abas compartilhem dados em tempo real.

Exemplo de Mensagens de Canal

// No primeiro contexto de navegação
const channel = new MessageChannel();
const port1 = channel.port1;
const port2 = channel.port2;

// Envia port2 para o segundo contexto de navegação
window.postMessage('init', '*', [port2]);

// Escuta mensagens na port1
port1.onmessage = (event) => {
  console.log('Mensagem recebida:', event.data);
};

// No segundo contexto de navegação
window.onmessage = (event) => {
  if (event.data === 'init') {
    const port2 = event.ports[0];
    port2.onmessage = (event) => {
      console.log('Mensagem recebida:', event.data);
    };
    port2.postMessage('Olá do segundo contexto!');
  }
};

Mensagens entre Documentos

As Mensagens entre Documentos, também conhecidas como postMessage, permitem que diferentes contextos de navegação (janelas, abas ou iframes) se comuniquem, mesmo que estejam em origens diferentes. Elas usam o método postMessage() para enviar um evento de mensagem ao contexto receptor, que pode então tratá-lo usando um ouvinte de eventos.

As Mensagens entre Documentos são úteis quando você precisa de mensagens unidirecionais entre contextos. Por exemplo, quando uma janela pai precisa que um iframe faça algo ou quando uma aba precisa enviar dados para outra aba.

Exemplo de Mensagens entre Documentos

// No contexto de envio
const targetWindow = window.parent; // Ou window.opener, ou document.getElementById('myIframe').contentWindow
targetWindow.postMessage('Olá do remetente!', '*');

// No contexto de recebimento
window.addEventListener('message', (event) => {
  if (event.origin === 'http://example.com') {
    console.log('Mensagem recebida:', event.data);
  }
});

Web Workers

Os Web Workers executam scripts em segundo plano sem afetar o desempenho da página. Eles permitem processamento pesado em uma thread separada para que a página principal permaneça responsiva.

Os Web Workers são úteis para tarefas que levam tempo, como cálculos complexos e processamento de dados.

Exemplo de Web Workers

// No script principal
const worker = new Worker('worker.js');
worker.onmessage = (event) => {
  console.log('Mensagem recebida do worker:', event.data);
};
worker.postMessage('Olá do script principal!');

// Em worker.js
self.onmessage = (event) => {
  console.log('Mensagem recebida no worker:', event.data);
  self.postMessage('Olá do worker!');
};

Considerações de Segurança

Ao usar a Comunicação Web, esteja ciente dos riscos potenciais de segurança e siga as melhores práticas para uma implementação segura.

Um dos principais riscos da Comunicação Web são os ataques de script entre sites (XSS). Se você não validar e limpar os dados recebidos através de mensagens, um atacante pode enviar código malicioso que será executado em sua página. Isso permitiria que ele roubasse dados sensíveis, realizasse ações em nome do usuário ou assumisse o controle da página.

Exemplo de código HTML para risco de XSS

<p>This    is   a   paragraph   with    extra   spaces.</p>

Quando um navegador renderiza este código, ele exibirá o texto como:

This is a paragraph with extra spaces.

Para prevenir ataques XSS, sempre valide e limpe qualquer dado recebido através de mensagens. Não assuma que os dados são seguros apenas porque vêm de outra janela ou worker que você controla. Trate todos os dados recebidos como entrada não confiável e lide com eles de acordo.

Outro risco é ao usar a Comunicação Web para comunicação entre diferentes origens. Tenha cuidado com quais origens você confia. Se você aceitar mensagens de qualquer origem sem verificar, um atacante poderia enviar mensagens de um site malicioso que enganaria sua página, fazendo-a realizar algo que não deveria. Para reduzir esse risco, sempre verifique a propriedade origin do evento de mensagem e aceite apenas mensagens de origens confiáveis.

Ao usar a Comunicação por Canais, saiba que qualquer contexto que receba uma porta de mensagem tem acesso total a esse canal. Isso significa que eles podem enviar mensagens no canal e até fechá-lo. Portanto, compartilhe portas de mensagem apenas com contextos em que você confia totalmente.

Com Web Workers, um dos principais riscos é que um worker pode travar ou usar muitos recursos se ficar preso em um loop infinito ou tentar fazer muito trabalho. Para evitar isso, use o método Worker.terminate() para parar um worker imediatamente, se necessário. Estruture seu código de worker para responder às mensagens rapidamente e evite tarefas de longa duração.

Exemplo: Usando o método Worker.terminate()

var myWorker = new Worker('worker.js');

// Encerra o worker após 5 segundos
setTimeout(function() {
    myWorker.terminate();
}, 5000);

Este código interrompe o worker após um tempo especificado para evitar o uso excessivo de recursos.

Aqui estão algumas melhores práticas para uma Comunicação Web segura:

  • Sempre valide e limpe os dados das mensagens recebidas
  • Aceite apenas mensagens de origens confiáveis
  • Tenha cuidado ao compartilhar portas de mensagem
  • Use Worker.terminate() para parar workers com mau comportamento
  • Mantenha seu código de worker focado e responsivo
  • Não use a Comunicação Web para compartilhar dados sensíveis, a menos que seja necessário
  • Use criptografia para dados sensíveis, se necessário
  • Teste minuciosamente seu código de Comunicação Web para possíveis vulnerabilidades

Suporte do Navegador

A maioria dos navegadores web modernos suporta Web Messaging. Isso inclui Chrome, Firefox, Safari e Internet Explorer 11 e versões superiores. A API WebSocket e os Eventos Enviados pelo Servidor, usados para mensagens em tempo real, também são suportados na maioria dos navegadores.

Navegadores mais antigos podem ter suporte limitado ou nenhum suporte para alguns recursos do Web Messaging. Por exemplo:

  • Internet Explorer 8 e versões anteriores não suportam o método window.postMessage() usado no Cross-Document Messaging.
  • Internet Explorer 10 e versões anteriores não suportam Web Workers.
  • Internet Explorer não suporta Channel Messaging.

Se você precisa dar suporte a navegadores mais antigos, pode ser necessário usar soluções alternativas ou fallbacks. Algumas opções incluem:

  • Para Cross-Document Messaging em versões antigas do IE, você pode usar uma biblioteca como easyXDM que fornece um transporte alternativo usando iframes e a propriedade location.hash.

Exemplo usando a biblioteca easyXDM

// No contexto de envio
var socket = new easyXDM.Socket({
  remote: "http://example.com/remote.html",
  onMessage: function(message, origin) {
    console.log("Mensagem recebida:", message);
  }
});
socket.postMessage("Olá do remetente!");

// No contexto de recebimento (remote.html)
var socket = new easyXDM.Socket({
  onMessage: function(message, origin) {
    console.log("Mensagem recebida:", message);
  }
});
  • Para Web Workers no IE10 e versões anteriores, você pode detectar o suporte e fornecer um fallback que execute o script na thread principal.

Exemplo de fallback para Web Workers

if (typeof(Worker) !== "undefined") {
  // Web Workers suportado, use worker
  var worker = new Worker("worker.js");
} else {
  // Web Workers não suportado, execute o script na thread principal
  var workerFallbackScript = document.createElement('script');
  workerFallbackScript.src = 'worker.js';
  document.body.appendChild(workerFallbackScript);
}

Para alternativas ao Channel Messaging: Você pode usar outras técnicas de mensagem como Cross-Document Messaging ou uma solução baseada em servidor quando o Channel Messaging não estiver disponível.

É importante testar seu código de Web Messaging em todos os navegadores que você planeja suportar. Use detecção de recursos para verificar o suporte e forneça fallbacks apropriados quando necessário para dar a todos os seus usuários uma boa experiência.

O cenário dos navegadores está sempre melhorando. A maioria dos usuários agora usa navegadores modernos com muitos recursos. Então, embora fallbacks para navegadores antigos ainda possam ser necessários às vezes, eles estão se tornando menos críticos ao longo do tempo à medida que o uso de navegadores legados continua diminuindo.

Exemplos Práticos

Aplicativo de Chat em Tempo Real

Criar um aplicativo de chat em tempo real mostra o poder da Mensagem Web. Aqui está um guia para criar um aplicativo de chat básico usando Mensagem de Canal e Web Workers:

  1. Configure a estrutura HTML para a interface do chat, incluindo um campo de entrada para mensagens e um contêiner para exibir o histórico do chat.

  2. Crie um novo Web Worker para lidar com o processamento e armazenamento de mensagens. Este worker receberá mensagens do script principal, as armazenará e enviará de volta o histórico de chat atualizado quando solicitado.

Exemplo: Web Worker para Chat (chat_worker.js)

let chatHistory = [];

self.onmessage = (event) => {
  if (event.data.type === 'newMessage') {
    chatHistory.push(event.data.message);
    self.postMessage({ type: 'updateHistory', history: chatHistory });
  } else if (event.data.type === 'getHistory') {
    self.postMessage({ type: 'updateHistory', history: chatHistory });
  }
};
  1. No script principal, crie um novo canal de Mensagem de Canal e envie uma das portas para o worker.

Exemplo: Script Principal (script.js)

const chatWorker = new Worker('chat_worker.js');
const channel = new MessageChannel();
chatWorker.postMessage({ type: 'init', port: channel.port1 }, [channel.port1]);
  1. Configure ouvintes de eventos para entrada de mensagens e comunicação do canal. Quando uma nova mensagem é inserida, envie-a para o worker através do canal. Ouça as atualizações do histórico do worker e exiba-as na interface do chat.

Exemplo: Ouvintes de Eventos e Mensagens

const messageInput = document.getElementById('messageInput');
const chatContainer = document.getElementById('chatContainer');

channel.port2.onmessage = (event) => {
  if (event.data.type === 'updateHistory') {
    const messagesHTML = event.data.history.map(message => `<p>${message}</p>`).join('');
    chatContainer.innerHTML = messagesHTML;
  }
};

messageInput.addEventListener('keypress', (event) => {
  if (event.key === 'Enter') {
    const message = messageInput.value;
    channel.port2.postMessage({ type: 'newMessage', message });
    messageInput.value = '';
  }
});
  1. Quando a página carrega, solicite o histórico inicial do chat ao worker.

Exemplo: Solicitar Histórico Inicial do Chat

chatWorker.postMessage({ type: 'getHistory' });

Com estes passos, você tem um aplicativo de chat em tempo real básico que usa Mensagem Web para comunicar entre o script principal e o Web Worker.

Comunicação Entre Domínios

A Mensagem Web também pode permitir a comunicação entre diferentes domínios usando Mensagem Entre Documentos:

  1. Na janela pai, crie um iframe com o atributo src definido para a URL da página com a qual você deseja se comunicar.

Exemplo: Janela Pai (parent.html)

<iframe id="myIframe" src="https://example.com/iframe.html"></iframe>
  1. No script do iframe, ouça eventos de mensagem e lide com eles com base no formato de dados esperado.

Exemplo: Script do Iframe (iframe.js)

window.addEventListener('message', function(event) {
  if (event.origin === 'https://example.com' && event.data.type === 'userData') {
      console.log('Dados do usuário recebidos:', event.data.value);
   }
});
  1. No script da janela pai, obtenha uma referência para a janela do iframe e envie mensagens usando o método postMessage().

Exemplo: Script da Janela Pai (parent.js)

const iframeWindow= document.getElementById('myIframe').contentWindow;
const userData= { name:'João Silva', age :30 };

document.getElementById("myIframe").addEventListener("load", () =>{
   iframeWindow.postMessage({type :'userData ', value :userData },'https://example.com ');
});

Com esta configuração, a janela pai pode enviar mensagens contendo dados do usuário para o iframe, e o iframe pode ouvir essas mensagens e processá-las adequadamente. Isso permite uma comunicação segura entre dois contextos, mesmo que estejam hospedados em domínios diferentes.

Ao usar Mensagem Entre Documentos, valide a origem das mensagens recebidas, garantindo que venham de fontes confiáveis, evitando ataques de script entre sites (XSS) e outras vulnerabilidades de segurança.